可编程逻辑器件FPGA学习-VHDL

VHDL源程序输入方式4种:

1.原理图:直观,形象,但是移植困难,交流困难,对于复杂对象描述比较困难

2硬件描述语言输入方式:纯文本方式,最普遍,易于修改、阅读和移植。

3状态图输入方式:可自动生成VHDL程序

4波形图输入方式

设计流程:

1提出设计说明书

2建立VHDL行为模型(编写VHDL源代码)

3逻辑仿真:对系统纯功能行为的考察,从顶层系统的行为模型进行测试

   仿真包括行为仿真、VHDL、RTL级建模、前端功能仿真

4逻辑综合:将VHDL描述转换为结构化的门电路。同样的功能可能有不同的实现形式和方法,这就是逻辑综合。

可编程逻辑器件FPGA学习-VHDL_第1张图片

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程序包(2STD_LOGIC_ARITH程序包(3STD_LOGIC_SIGNED程序包(4STD_LOGIC_UNSIGNED程序包

CONFIGURATION描述层与层之间的连接关系以及实体与构造体之间的对应关系。

1.VHDL程序基本结构

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的值进行或运算决定。

1基本数据类型

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位,有些情况下太浪费了  

1.1 bit 和bit_vector

ex:   signal x:bit; signal y:bit_vector(3 downto 0);

位宽为1时,位值放在单引号中;为宽大于1时,位矢量值放在双引号中。

1.2std_logic和std_logic_vector

ex:   signal x:std_logic;

ex:   signal y:std_logic_vector(3 downto 0):="0000";

1.3integer,natural和real

ex:   signal x:integer:=1;

1.4character

1.5signed和unsigned

2、常量、变量及标识符

2.1标识符

2.1.1定义:用来标识常量、变量、函数、过程等的字符序列

2.1.2命名规则

  • 只能由字母、数字、下划线组成。
  • 必须以字母或下划线开头,不以下划线或关键字结束。
  • 最多只能有一个下划线。
  • VHDL的关键字不能用作变量名。
  • VHDL语言对英文字母的大小写不敏感,即同一个字母的大小写,被认为是同一个字符。

2.2常量

2.2.1定义:程序运行时不能改变的量(即常数)

2.2.2分类:

a值常量: 主要包括逻辑常量(‘0’=逻辑0 ,‘1’=逻辑真),数值常量(35, “1100”)

b符号常量:用标识符来表示常量,也称为常变量。

其定义格式为  constant  符号常量名:  类型:= 符号常量值 ;

2.3变量:代表了电路中的管脚或寄存器。

2.3.1定义:在程序运行时,其值可以改变的量。

      存储属性  变量名:类型名 [模式] [:=初始值];

注意:所有的变量遵循先定义后使用的原则。

2.3.2变量存储属性

静态:值不可变;动态(非静态):值可变。

全局:可在顺序执行的代码中,也可在并发执行的代码中。

局部:只能在顺序执行的代码中,且不能对外传递。

  全局 局部
静态

generic(泛式常变量)

constant(常变量)

 
非静态 signal(信号变量) varible(可变变量)

另外还要注意在顺序代码中信号变量的更新不是及时的,只有在process、function或procedu执行完才更新,而可变量是即时更新的,它只能应用于process、function或procedu中。

2.3.3变量的模式:

对于信号变量还有模式,主要用于定义实体的管脚或过程的参数属性,共有4种模式:in,out,inout或buffer。in表示输入,out表示输出,inout表示双向输入输出,buffer表示输出,同时可以供电路内部使用。

2.3.4变量的定义与初始化:

generic(变量名:数据类型:=值);

constant 变量名:数据类型:=值;

signal 变量名:数据类型:=值;

varible 变量名:数据类型:=值;

2.3.5非静态变量的赋值

信号变量: 信号变量名<=值;
可变变量: 可变变量名:=值;

2.4数据类型转换

VDHL语言程序中,不同类型的数据不能进行算术或逻辑运算。因此有必要进行数据类型转换。有两种常见的方法可以实现数据类型转换:一种方法是写一段专门用于数据类型转换的VDHL代码;另一种是调用包集中预定义的数据类型转换函数。在包std_logic_arith中有一些数据类型转换函数,可以直接使用。

  • conv_integer(p):将p转换成整数。
  • conv_unsigned(p,b):将p转换成宽度为b的无符号数。
  • conv_signed(p,b):将p转换成宽度为b的有符号数。
  • conv_std_logic_vector(p,b):将p转换成宽度为b的std_logic_vector。

3运算符、属性符与表达式

3.1运算符

VHDL提供了6种预定义的运算符

赋值运算 <=         信号专用赋值
:=  初始化使用,或其他三类数据赋值。
=> 矢量中位赋值
逻辑运算 and、or、not  
nand  
nor、xor、xnor  
算术运算 +、 - 、* 、/  
** 指数运算
modremabs 模运算、余运算、绝对值
关系运算 =、/=、> 、<、 >=、<=  
移位运算 SLLSRL 逻辑移位
SLASRA 算术移位
ROLROR 循环移位
并置运算 &,(,,,) 用于位的拼接

3.1.2运算符扩展:略

3.2属性符:获取指定的客体或对象的特定数据或信息。

预定义属性 数值属性

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代表信号变量。

3.3表达式

4并发执行结构:组合逻辑电路结构

本质上VHDL代码是并发执行的,只有process、function和procedure内部的代码是顺序执行的,但是作为一个整体,与其他模块之间又是并发执行的。并发执行的语句综合后生成的电路与代码书写的顺序是无关的。并发结构主要实现组合逻辑,顺序结构实现时序逻辑。

常用的并行语句有进程语句(进程语句作为一个整体与其他语句之间是并行的关系)、并行信号幅值语句(简单信号赋值语句、选择信号赋值语句、条件信号赋值语句)、元件例化及生成语句等。

4.1赋值语句

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指明这一点。

4.2原句例化

4.3Generate语句

通常与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中循环的上下界必须是静态的。

4.4block语句:

主要是一种组织代码的方式,用于增强整个代码的可读性和可维护性,且可以嵌套。

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成立时执行,可构造时序电路,但是比较少见。

5顺序执行结构:时序电路结构

5.1进程 process:

process内部的代码是顺序执行的,经常使用if、wait、case或loop语句,它必须包含在主代码段中。进程有敏感性列表,当敏感性列表中的某个信号发生变化或当wait语句的条件得到满足时,process内部的代码就顺序执行一次。

如果在process内部使用变量,则必须在begin之前声明并定义,变量的初始值是不可综合的,只是在仿真过程中使用。

label:process(sensetive list)

variable name[ range][:=initial_value];

begin

...................................

end process;

5.2分支语句:2种

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不可以。

5.3循环语句:4种

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不能有敏感列表。

5.4exit与next

5.4.1exit语句

相当于C语言中的break语句,结束整个循环操作。主要有3种形式。标号代表下一次迭代的起始位置。

exit label;--相当于C语言中的无条件goto语句

label:exit [label] [when 条件表达式];--相当于C语言中的有条件goto语句

5.4.2 next语句

相当于C语言中的continue语句,跳出本次循环。标号代表下一次迭代的起始位置。如果next语句后面既无标号也无条件,则只要执行到该语句就立刻无条件跳出本次循环,从loop语句的起始位置进入下一次循环。

label:next [label] [when 条件表达式];

6子程序:函数和过程

这一部分的内容主要是为了实现代码的共享和复用。函数和过程一般在包package的body部分或结构architecture的声明部分定义。

6.1函数:返回值只有一个

函数的定义:

function function_name( [parameter list]) return data_type

is

       [声明]

begin

      (顺序描述代码)

end function_name;

其中parameter list指出了函数的输入参数,输入参数可以是常量或信号,具体描述为:

=[constant] 常量名:常量类型;

=signal 信号名:信号类型;

函数的引用:

函数可以单独构成表达式,也可以作为表达式的一部分。

6.2过程:多个返回值

函数的定义:

procedure procedure_name( [parameter list]) 

is

       [声明]

begin

      (顺序描述代码)

end procedure_name;

其中parameter list指出了函数的输入参数,输入参数可以是常量、变量或信号,具体描述为:

=[constant] 常变量名:模式

=signal 信号变量名:模式

=variable 可变变量名:模式

6.3信号变量与可变变量的区别

信号变量和可变变量仅仅是作用范围的不同吗?完全可以都采用全局变量嘛!实际情况并非如此。变量区分全局变量与局部变量问题处理解决变量管理及命名问题(变量太多,起个名字也是麻烦事)外,在VHDL中它们还有其他重要的区别。

7包及元件

7.1包

为实现代码的可重用性,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中有对应的描述代码。注意元件是不在包中定义(描述)的。

7.2元件

7.2.1元件的定义

一段结构完整的代码(包括库声明、实体和结构)代码就表示一个电路。如果在某处(一般是包文件,或调用的实体的结构的声部分中)将该电路声明为元件(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; 结构定义结束

7.2.2元件的声明以及实例化

要使用一个元件(实例化),首先要对这个元件进行声明。声明一个元件实际上就是告诉系统元件的输入、输出管脚有何特性,如何在新电路(系统)中使用该元件。

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; 若没有进行封装,则直接进行声明。

7.2.3端口映射及参数映射

所谓映射就是解决在元件实例化时如何使用元件的管脚及泛化参数(元件规模)问题,这分别对应了端口映射和参数映射问题。被引用的元件的输入输出管脚及泛化参数对引用的元件来说相当于相当于形参与实参问题。

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);

若泛化参数不明确指出,则用缺省参数。

 

8复杂数据类型

复杂数据类型要在architecture部分(或package中)声明和定义。

8.1array

组织同类型数据的方法。

8.1.1声明

type type_name is array [specifaction] of data_type;

 8.1.2定义

存储类型  变量名:类型名[:=初始值];

    ex:  type matrix2D  is array(0 to 3, 7 downto 0) of std_logic;

    引用数组元素:x<=y(0)(1);

8.2record

组织不同类型数据的方法,先定义一种新的数据类型。

type birthday is record

       day:integer range 1 to 31;

        month:month_name;

end record;

8.3枚举

用名称可能比用数字更清楚直观,易于理解。

type color is (red,green,blue,white);--red='00",green="01",blue="10",white="11";

8.4subtype

已有的数据类型太耗费资源,定义一个子集以节省资源。不同类数据不能直接计算,但是在已有类型的基础上加以限制形成的子类,可以和父类直接运算。

subtype small_interge is interger range -32 to 30;

8.5自定义整数类型

已定义的整数类型是32位的,对某些场合可能太大了,需要32个寄存器,节省资源。

  type  type_name  range   low  to hight;

ex:type student_grade  range 0 to 100;

自定义整数类型和子类非常像,但是两个差别:

  1. 子类和父类是可以直接运算的。
  2. 子类的每个元素不一定是整数,自定义整数类型的每个成员一定是整数。

8.6.数组的特殊用法:端口数组

基本的数据类型都没有超过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;

 

可编程逻辑器件FPGA学习-VHDL_第2张图片

 

你可能感兴趣的:(嵌入式系统)