VHDL源程序输入方式4种:
1.原理图:直观,形象,但是移植困难,交流困难,对于复杂对象描述比较困难
2硬件描述语言输入方式:纯文本方式,最普遍,易于修改、阅读和移植。
3状态图输入方式:可自动生成VHDL程序
4波形图输入方式
设计流程:
1提出设计说明书
2建立VHDL行为模型(编写VHDL源代码)
3逻辑仿真:对系统纯功能行为的考察,从顶层系统的行为模型进行测试
仿真包括行为仿真、VHDL、RTL级建模、前端功能仿真
4逻辑综合:将VHDL描述转换为结构化的门电路。同样的功能可能有不同的实现形式和方法,这就是逻辑综合。
5测试向量生成:用于ASIC功能测试
6功能仿真和结构综合:结合硬件和测试优化结构配置。同样的功能,采用同样的实现形式,其具体的电路布线、布局也是有不同的形式和方法的。
7时序仿真:将带有从布局布线得到的精确时序信息映射到门级电路进行仿真,以检查电路时序,并对电路进行最后检查。
8硬件测试
现在看4、5、6、7项暂时好像用不到,因为不搞ASIC设计,VHDL就是编程。VHDL编好,然后编译,在下到实验箱中,OK?
*.xise:xlinux工程文件
*.vhd :VHDL文件
*.bit:综合后的JTAG下载文件,一般下到FPGA的RAM中
*.mcs:PROM文件的一种,由*.bit转化而来,将烧写到flash中
VHDL有module文件,library文件,package文件和test Bench文件四种。
完整的VHDL语言程序通常包含实体(ENTITY),结构体(ARCHITECTURE),库(LIBRARY),程序包(PACKAGE)和配置(CONFIGURATION)5个部分。但是5个部分并不是都必须的。
ENTITY 相当于函数声明,是系统的外部描述接口.
ARCHITECTURE相当于函数定义,描述系统的内部实现细节.
LIBRARY是专门用于存放预先编译好的程序包的地方,相当于C语言中*.lib。常用的库有IEEE库、STD库和WORK库。
PACKAGE是用VHDL语言编写的一段程序,可以供其他设计单元调用和共享,类似于C语言的头文件。调用程序包的通用模式为:USE 库名.程序包名.ALL;
常用预定义程序包有以下四个:(1)STD_LOGIC_1164程序包(2)STD_LOGIC_ARITH程序包(3)STD_LOGIC_SIGNED程序包(4)STD_LOGIC_UNSIGNED程序包
CONFIGURATION描述层与层之间的连接关系以及实体与构造体之间的对应关系。
VHDL代码实际上就是描述了一个系统、电路或元件。其主要包含三个部分库声明、实体和构造。
库声明:列出了当前设计所需的库文件。
library ieee; --使用的库
use IEEE.STD_LOGIC_1164.ALL; --使用的包
实体:定义了电路的输入输出管脚。
Entity exam Is --声明实体的名称
Generic(width: Interge : =42; --实体中所属常数,该参数数的改变
--将改变设计结果的规模。
width1:Interge:=16)
Port ( A: In Std_logic_vector(width-1 Downto 0); --实体的输入
B :Out Std_logic_vector(width1-1 Downto 0); --实体的输出
C :Inout Std_logic_vector(width1-1 Downto 0); --实体的输入输
--出,可实现双向输出
D :Buffer Std_logic_vector(width1-1 Downto 0))--缓冲输出,并且实体
--内部可引用
End exam; --表明实体结束
构造:描述了电路要实现的功能。
architecture behavior of MyGate is --定义结构名称及所属实体
singal temp1,temp2:BIT; --说明内部用到的信号、常数、函数等
begin --开始描述
z<=a OR b; --功能描述
end behavior; --结束该结构描述
典型的VHDL例子,实现OR功能。
--This is the first VHDL program
library ieee;
use IEEE.STD_LOGIC_1164.ALL;
entity myGate is
Port ( a : in STD_LOGIC;
b : in STD_LOGIC;
z : out STD_LOGIC);
end myGate;
architecture behavior of MyGate is
begin
z<=a OR b;
end behavior;
程序解释:
程序的第1行是注释信息。注释是为了增加程序的可读性和易懂性。注释以--开头。
第2行是库的声明。库是专门用于存放预先编译好的程序包的地方,相当于C语言中*.lib。常用的库有IEEE库、STD库和WORK库。
第3行是包的声明。包是用VHDL语言编写的一段程序,可以供其他设计单元调用和共享,类似于C语言的头文件。调用程序包的通用模式为:USE 库名.程序包名.ALL;
第4~8行是实体的声明。实体相当于函数声明,描述系统(电路)的外部接口。其中第4行声明了一个名为myGate的实体。第5~7行声明了电路的三个引脚,两个输入(a和b),一个输出(z),它们都是STD_LOGIC型的。第8行表示myGate实体声明结束。
第9行为空行。
第10~13是结构的声明。结构相当于函数的定义(实现),描述系统(电路)的内部实现细节。第10行声明本结构为myGate实体的结构,其名称为 behavior。第11~13行表示具体的实现细节(相当于高级语言中的函数体)。其中第11、13行表示界定,第12行表示输出管脚z的结果由将输入管脚a和b的值进行或运算决定。
VHDL语言程序在处理数据之前,要求数据必须具有明确的数据类型。所谓数据类型是按被说明量的性质、表示形式等特点来划分的。
基本类型(预定义类型) | 逻辑型 | BIT | '0' ,'1' | 二进位类型 |
BIT_VECTOR | 位向量类型,表示一组二进制数,常用来描述地址总线、数据总线等 | |||
STD_LOGIC | 'X','0','1','Z','W','L','H','_' | 工业标准的逻辑类型 | ||
STD_LOGIC_VECTOR | 工业标准的逻辑向量类型 | |||
STD_ULOGIC | 比上述多1个‘U’- | |||
STD_ULOGIC_VECTOR | ||||
BOOLEAN | true, false | |||
数值型 | INTERGE | 32位有符号整数 | ||
NATURAL | 32位无符号整数 | |||
REAL | 32位ieee浮点数,不可综合。 | |||
字符型 | CHARACTER | 1个或多个字符串 | ||
算术型 | SIGNED | 与std_logic_vector类似,但是其支持算术运算,使用需要在代码开头声明ieee库中的包std_logic_arith,才能使用。 | ||
USIGNED | ||||
时间型 | 不可综合,用于仿真 | |||
复杂类型(构造类型,用户自定义类型) | type | 数组(同类型数据组合)ARRAY | ||
端口数组 | entity中不能进行类型定义,但是其输入输出有时候组织作为一个整体更高效 | |||
记录(不同类型数据组合)RECORD | ||||
枚举类型 | ||||
子类型subtype | 不同类数据不能直接计算,但是在已有类型的基础上加以限制形成的子类,可以和原类可以直接运算 | |||
自定义整数类型 | 标准整数是32位,有些情况下太浪费了 |
ex: signal x:bit; signal y:bit_vector(3 downto 0);
位宽为1时,位值放在单引号中;为宽大于1时,位矢量值放在双引号中。
ex: signal x:std_logic;
ex: signal y:std_logic_vector(3 downto 0):="0000";
ex: signal x:integer:=1;
a值常量: 主要包括逻辑常量(‘0’=逻辑0 ,‘1’=逻辑真),数值常量(35, “1100”)
b符号常量:用标识符来表示常量,也称为常变量。
其定义格式为 constant 符号常量名: 类型:= 符号常量值 ;
存储属性 变量名:类型名 [模式] [:=初始值];
注意:所有的变量遵循先定义后使用的原则。
静态:值不可变;动态(非静态):值可变。
全局:可在顺序执行的代码中,也可在并发执行的代码中。
局部:只能在顺序执行的代码中,且不能对外传递。
全局 | 局部 | |
静态 | generic(泛式常变量) constant(常变量) |
|
非静态 | signal(信号变量) | varible(可变变量) |
另外还要注意在顺序代码中信号变量的更新不是及时的,只有在process、function或procedu执行完才更新,而可变量是即时更新的,它只能应用于process、function或procedu中。
对于信号变量还有模式,主要用于定义实体的管脚或过程的参数属性,共有4种模式:in,out,inout或buffer。in表示输入,out表示输出,inout表示双向输入输出,buffer表示输出,同时可以供电路内部使用。
generic(变量名:数据类型:=值);
constant 变量名:数据类型:=值;
signal 变量名:数据类型:=值;
varible 变量名:数据类型:=值;
信号变量: | 信号变量名<=值; |
可变变量: | 可变变量名:=值; |
VDHL语言程序中,不同类型的数据不能进行算术或逻辑运算。因此有必要进行数据类型转换。有两种常见的方法可以实现数据类型转换:一种方法是写一段专门用于数据类型转换的VDHL代码;另一种是调用包集中预定义的数据类型转换函数。在包std_logic_arith中有一些数据类型转换函数,可以直接使用。
VHDL提供了6种预定义的运算符
赋值运算 | <= | 信号专用赋值 |
:= | 初始化使用,或其他三类数据赋值。 | |
=> | 矢量中位赋值 | |
逻辑运算 | and、or、not | |
nand | ||
nor、xor、xnor | ||
算术运算 | +、 - 、* 、/ | |
** | 指数运算 | |
mod 、rem、abs | 模运算、余运算、绝对值 | |
关系运算 | =、/=、> 、<、 >=、<= | |
移位运算 | SLL,SRL | 逻辑移位 |
SLA,SRA | 算术移位 | |
ROL,ROR | 循环移位 | |
并置运算 | &,(,,,) | 用于位的拼接 |
预定义属性 | 数值属性 | d'HIGHT,d'LOW,d'LEFT,d'RIGHT, d'LENTH,d'RANNGE,d'REVERSE_RANG, d'VAL(pos),d'POS(vauel),d'LEFTOF(value), d'VAL(row,column) |
大部分属性仅用于仿真,s'EVENT和s'STABLE可以综合 |
信号属性 | s'EVENT,s'STABLE,s'ACTIVE, s'QUITE s'LAST_EVENT,s'LAST_ACTIVE,s'LAST_VALUE |
||
用户自定义属性 |
*注意:d,s表示变量名,其中s代表信号变量。
本质上VHDL代码是并发执行的,只有process、function和procedure内部的代码是顺序执行的,但是作为一个整体,与其他模块之间又是并发执行的。并发执行的语句综合后生成的电路与代码书写的顺序是无关的。并发结构主要实现组合逻辑,顺序结构实现时序逻辑。
常用的并行语句有进程语句(进程语句作为一个整体与其他语句之间是并行的关系)、并行信号幅值语句(简单信号赋值语句、选择信号赋值语句、条件信号赋值语句)、元件例化及生成语句等。
4.1.1简单信号赋值语句
4.1.2when语句-条件信号赋值语句
4.1.3with-select语句-选择信号赋值语句
两种结构: when/else和with/select/when结构
when-else结构 | with -select-when结构 |
assignment when condit else assignment when condit else .............................................; |
with identifier select assignment when value, assignment when value, .....................................; |
--when-else output<="000" when (inp='0' or reset='1') else "001" when (ctl='1') else "010"; |
--with-select with control select outp<="000" when reset, "111" when ctl, unaffacted when others; |
必须对所有的条件加以考虑,经常要使用others关键字,如果在某些条件下不需要执行任何操作,应用unaffected指明这一点。
通常与for一起使用,执行某种循环操作,有两种结构
for-generate | if-generate |
label:for identifier in range generate (concurrent assignments) end generate; |
label:if condition generate (concurrent assignments) end generate; |
g1: for i in x'range generate z(i)<=x(i) and y(i+8); end generate; |
|
1.二者可以相互嵌套 2.if-gennerate结构是允许使用else的 3.generate中循环的上下界必须是静态的。 |
主要是一种组织代码的方式,用于增强整个代码的可读性和可维护性,且可以嵌套。
simple block | guarded block |
label:block [declarative part] begin (concurrent statement) end block label; |
label:block(guarded expression) [declarative part] begin (concurrent statement) end block label; |
当guarded expression成立时执行,可构造时序电路,但是比较少见。 |
process内部的代码是顺序执行的,经常使用if、wait、case或loop语句,它必须包含在主代码段中。进程有敏感性列表,当敏感性列表中的某个信号发生变化或当wait语句的条件得到满足时,process内部的代码就顺序执行一次。
如果在process内部使用变量,则必须在begin之前声明并定义,变量的初始值是不可综合的,只是在仿真过程中使用。
label:process(sensetive list) variable name[ range][:=initial_value]; begin ................................... end process; |
if语句
if condition then assignment; elsif condition then assignment; .............................................. else assignment; end if; |
case语句 相当于C语言中的switch语句。
case 表达式 is when 条件表达式=>顺序执行语句; when 条件表达式=>顺序执行语句; ................................... end case; |
与when语句类似,要考虑所有情况,关键字others非常有用,它代表所有未列出的情况。但是when语句是并发执行的,case是顺序执行的,可以在process、function和procedu中使用,而when不可以。
for型 | label:for 循环变量 in 范围 loop 顺序描述语句; end loop [label]; |
固定次数循环 |
while型 | label:while 条件表达式 loop 顺序描述语句; end loop [label]; |
循环次数不定 |
do型 | label:loop ...... exit when 条件表达式; end loop; |
至少执行一次 |
wait型 | wait until signal_condition; | 直到信号条件满足才执行后面的操作。 |
wait on signal1 [,singal2,...]; | 当后面列出的信号有一个发生变化时,才执行后面的操作。 | |
wait for time; | 等待time所确定的时间才开始执行后面的操作。 |
注意:wait直到条件满足时才会执行后面的操作,否则一直等待,相当于阻塞。若process中采用了wait语句,则process不能有敏感列表。
相当于C语言中的break语句,结束整个循环操作。主要有3种形式。标号代表下一次迭代的起始位置。
exit label;--相当于C语言中的无条件goto语句
label:exit [label] [when 条件表达式];--相当于C语言中的有条件goto语句
相当于C语言中的continue语句,跳出本次循环。标号代表下一次迭代的起始位置。如果next语句后面既无标号也无条件,则只要执行到该语句就立刻无条件跳出本次循环,从loop语句的起始位置进入下一次循环。
label:next [label] [when 条件表达式];
这一部分的内容主要是为了实现代码的共享和复用。函数和过程一般在包package的body部分或结构architecture的声明部分定义。
函数的定义:
function function_name( [parameter list]) return data_type is [声明] begin (顺序描述代码) end function_name; |
其中parameter list指出了函数的输入参数,输入参数可以是常量或信号,具体描述为: |
函数的引用:
函数可以单独构成表达式,也可以作为表达式的一部分。
函数的定义:
procedure procedure_name( [parameter list]) is [声明] begin (顺序描述代码) end procedure_name; |
其中parameter list指出了函数的输入参数,输入参数可以是常量、变量或信号,具体描述为: |
信号变量和可变变量仅仅是作用范围的不同吗?完全可以都采用全局变量嘛!实际情况并非如此。变量区分全局变量与局部变量问题处理解决变量管理及命名问题(变量太多,起个名字也是麻烦事)外,在VHDL中它们还有其他重要的区别。
为实现代码的可重用性,VHDL中可以将以function、procedure等形式的代码,封装到package中,形成文件,编译到library中,供他人调用。除了function、procedure外,package还可包括元件、复杂数据类型及常量的声明等。语法格式:
package package_name is [元件、函数、过程,复杂数据类型及常量的声明] end package_name; [package body package_name is 函数、过程的定义; end package_name;] |
声明的函数、过程必须在package body中有对应的描述代码。注意元件是不在包中定义(描述)的。
一段结构完整的代码(包括库声明、实体和结构)代码就表示一个电路。如果在某处(一般是包文件,或调用的实体的结构的声部分中)将该电路声明为元件(component),就可以被其他电路(或系统)调用,从而使代码具有了层次化的结构。元件是一种进行代码分割、代码共享和代码重用的方法。可以将常用的电路放到一个库(library)中,供所有设计者使用。注意元件的定义是不在任何包中的。
library ieee; use ieee.std_logic_1164.all; |
使用的库、包等 |
Entity entity_name is | 实体定义开始 |
[generic();] | 泛化参数,可选 |
port(port_list); | 输入、输出管脚 |
end entity_name; | 实体定义结束 |
architecture architecture_name of entity_name is | 结构定义开始 |
[declarations] | 声明部分,声明电路将引用的局部变量、常变量及元件等。 |
begin | 结构主体开始 |
(code) | 并发语句 |
end architecture_name; | 结构定义结束 |
要使用一个元件(实例化),首先要对这个元件进行声明。声明一个元件实际上就是告诉系统元件的输入、输出管脚有何特性,如何在新电路(系统)中使用该元件。
component component_name is port( prot_name:signal_mode signal_type; prot_name:signal_mode signal_type; ...); end component; |
实例化的方法:
label:component_name port map (port_list); |
电路中要使用某个元件必须首先声明,然后在实例化。元件的实例化一定是在电路的结构部分,而元件若是封装在某个包中(如mypackage)则需要在电路的开头进行声明,如 :use work.mypackage.all; 若没有进行封装,则直接进行声明。
所谓映射就是解决在元件实例化时如何使用元件的管脚及泛化参数(元件规模)问题,这分别对应了端口映射和参数映射问题。被引用的元件的输入输出管脚及泛化参数对引用的元件来说相当于相当于形参与实参问题。
Entity invert is generic(n:integer:=7); port(a:in bit; b: out bit); end invert; architecture invert of invert is ........................ begin ......................... end invert; |
端口映射 | 位置映射 | component invert is port(a:in bit; b: out bit); end component; ................. u1:invert port map(x,y); |
简单 | 对不需要的端口可以使用关键字open断开 |
名称映射 | component invert is port(a:in bit; b: out bit); end component; ................. u1:invert port map(a=>x,b=>b); |
不易出错 | |||
参数映射 | ----------声明 component invert is generic(n:integer); port(a:in bit; b: out bit); end component; .............................. -------实例化 label:component_name generic map(n=>2); port map(port_list); |
若泛化参数不明确指出,则用缺省参数。 |
复杂数据类型要在architecture部分(或package中)声明和定义。
组织同类型数据的方法。
type type_name is array [specifaction] of data_type;
存储类型 变量名:类型名[:=初始值];
ex: type matrix2D is array(0 to 3, 7 downto 0) of std_logic;
引用数组元素:x<=y(0)(1);
组织不同类型数据的方法,先定义一种新的数据类型。
type birthday is record day:integer range 1 to 31; month:month_name; end record; |
用名称可能比用数字更清楚直观,易于理解。
type color is (red,green,blue,white);--red='00",green="01",blue="10",white="11";
已有的数据类型太耗费资源,定义一个子集以节省资源。不同类数据不能直接计算,但是在已有类型的基础上加以限制形成的子类,可以和父类直接运算。
subtype small_interge is interger range -32 to 30;
已定义的整数类型是32位的,对某些场合可能太大了,需要32个寄存器,节省资源。
type type_name range low to hight;
ex:type student_grade range 0 to 100;
自定义整数类型和子类非常像,但是两个差别:
基本的数据类型都没有超过1维的,而在定义电路输入输出端口时,有时候要把端口定义为矢量数组(如MCS51中4组IO端口,每组端口又包括8个管脚),但是在Entity中是不允许使用type进行类型定义,所有必须在包中根据端口的具体信号特征建立用户自己的数据类型,该数据类型可供包括entity在内的整个设计使用。
------------------------------------包-------------------------------------- library ieee; use ieee.std_logic_1164.all; package myDataTypes is ................................. type vector is array(natural range<>) of std_logic_vector(7 downto 0); .......................................... end myDataTypes; |
------------------------------------主代码----------------------------------------- library ieee; use ieee.std_logic_1164.all; use work.myDataTypes.all; entity mux is port( inp: in vector(0 to 3); .......); end mux; |