当前位置:首页 > 新闻资讯 > FPGA之家动态 >

FPGA高级编程语言VHDL基本语法讲解之— 8位加法器

时间:2024-07-30      来源:网络搜集 关于我们 0

在头条写了这么久文章,今天突发奇想,想写一篇关于VHDL的文章,因为这个也是我用的比较多,并且非常熟悉的FPGA高级编程语言,不知道有没有人看,因为我不知道今日头条上有多少从事电气控制,电子相关领域的同行。

要是有人看,以后可以多写写这方面的文章,大家一起相互讨论,相互学习,共同进步。

VHDL语言简介

首先对VHDL语言做一个简单介绍,可能有些人没听过,或者听过但是没有用过,但是,只要知道或者学过FPGA的朋友肯定都是知道的,但是由于国内公司内使用的比较少,普及程度可能不是很高。作为一名合格的自动化控制工程师,我们得对一些专业缩写名词的全称要知道,不然出去别人一问不就露馅了,还是有点丢人啊。

这也是找工作的时候,经常会被问到或者笔试会考到的问题。所以呢,先说下VHDL全名,在这里我敢打赌,就算是专业从事FPGA相关工作的人员,应该也有不少人写不出来VHDL全名。不信你可以拿出一张纸写一写,能够写出来文章后面留言。

VHDL全名为:Very High Speed Integrated Circuit Hardware Description Language,翻译过来是超高速集成电路硬件描述语言。

其中Very High Speed Integrated Circuit又可以简写为(VHSIC)翻译过来是超高速集成电路的意思。

这就是说,我们在完全写不出来的时候,也可以将VHDL全名写为更简单一点的:

VHSIC Hardware Description Language),别人肯定不会说我们错啊,VHDL全名这样写肯定是没有任何问题。VHSIC后面的Hardware Description Language 要是都写不出来,那就真没救了。

既然已经提到了FPGA,这里也写出其全称为:(Field-Programmable Gate Array),即现场可编程门阵列),现在我们更多的可以把它理解为一个可编程芯片,并且可以对其进行编程就可以了,简单粗暴易懂。

下面介绍下我比较钟爱的VDHL语言的来历,她出生于1982年,诞生在美国国防部大家庭,足以可见其出生高贵,这也为她后来成长为一个严谨并且不失高雅,清新脱俗的大家闺秀打下了一个坚实的基础。刚出生时候没有什么名气,默默成长几年以后,在1987年,终于具有了自己的名分。被IEEE与她的娘家美国国防部确认为一个新的高级编程语言标准。

并且随着她不断长大,经过一代代脱胎换骨,就成了我们今天看到的被广泛使用的硬件编程语言之一。因为,她还有一个姐妹,名字叫做Verilog HDL,在行业内也很受欢迎,今天不对她做介绍。

今天在这里主要通过一个使用VHDL语言编写的8位全加器作为例子。讲一些VHDL的基本语法以及程序结构。程序如下所示:

library ieee;

use ieee.std_logic_1164.all;

use ieee.std_logic_unsigned.all;

entity Adder8 is

port (in1,in2 : in std_logic_vector(7 downto 0);

cin : in std_logic;

cout : out std_logic;

pout : out std_logic_vector(7 downto 0) );

end entity Adder8;

architecture Func of Adder8 is

signal data :std_logic_vector(8 downto 0);

begin

process(in1,in2,cin)

begin

data <= (0 & in1) + (0 & in2) + ("00000000" & cin);

cout <= data(8);

pout <= data(7 downto 0);

end process;

end architecture Func ;

主要将程序分为以下几个部分讲解:有关库文件部分、有关实体(entity)部分、有关结构体(architecture )部分(有关进程(process )部分)

第一部分为:有关设计库文件声明部分

library ieee;

use ieee.std_logic_1164.all;

use ieee.std_logic_unsigned.all;

关于库文件说明,只要有点编程基础的人都可以很容易理解,也就是在编写程序前,申明在我们接下来的程序中将会用到哪些库或者包里面的函数或者变量等,经过说明后,实体和结构体就可以自动调用库中的资源。

程序包相当于一个公用的“工具箱”,可以类比我们在C语言里面的编程最前面的# include <...>部分,或者python语言里的最前面加的 import math 等。

其中 ieee其实只是VHDL所有库中的一种,其它还有如STD库 、ASIC库、 work库 、用户定义库,库内部包含有子库或者程序包。

比如std_logic_1164 就属于ieee标准库中的一个程序包。声明中的xxxx.all表示范围(或项目名)

library ieee; 就是说明下面程序用到的总库为ieee,只要用到ieee库,就必须使用 library ieee进行说明,在使用到其它库时同样的道理。比如有:

LIBRARY ieee;

USE ieee.std_logic_1164.all;

USE ieee.std_logic_unsigned.all;

USE ieee.std_logic_arith.all;

LIBRARY work;

USE work.mypackage.all;

USE work.myfunction.all;

use ieee.std_logic_1164.all; 就是说明使用了use 调取了ieee库中的程序包 std_logic_1164,进行了调用前的申明,use

ieee.std_logic_unsigned.all; 一样的道理,也是ieee中的包。

为什么需要调用std_logic_unsigned包呢,因为这里面有我们下面用到的实现加法的“+”,当然需要在这里进行申明,明白了吧。

library ieee;

use ieee.std_logic_1164.all;

第二部分为:有关实体(entity)部分

实体的主要功能为:描述与外部电路的接口,比如哪些是输入接口、哪些是输出接口等。

实体:以关键字entity引导...中间为port() ... 以end entity结尾的语句部分。中间为实体内容,Adder8为实体名,我们要为每一个实体起一个唯一的名字,并且要与文件名一致,这个是非常重要的。

port(...)说明了端口,这个端口可以对应实际的PFGA芯片的引脚

entity Adder8 is

port (in1,in2 : in std_logic_vector(7 downto 0);

cin : in std_logic;

cout : out std_logic;

pout : out std_logic_vector(7 downto 0) );

end entity Adder8;

端口定义方法: 端口名:端口模式(in/out/buff/inout) 数据类型;

例程中: in1,in2是定义的两个端口名,数据类型为 std_logic_vector。cin也是一个端口,数据类型为std_logic。

例程中: pout也是定义的端口名,数据类型为 std_logic_vector。cout也是一个端口,数据类型为std_logic。

in:指输入端口,只能由外向内传入信号与数据,也就是说只能出现在赋值语句右边。

out:指输出端口,只能由内向外传出信号与数据,也就是说只能出现在赋值语句左边。具体看下面程序进程讲解部分。

总之一句话,端口就是:设计实体与外部环境动态通信的通道。

std_logic_vector //表示逻辑向量,也相当于一组逻辑位,比如"11110011"。

std_logic //表示逻辑位,比如“1”,“0”等。

而数据类型后面的(7 downto 0),就是指数据是从高到底排列的,in1[7]... ...in2[0],总共是8位数据。

std_logic_vector 与std_logic 两个数据类型是属于开始申明的标准数据包std_logic_1164的两个最基本的数据类型,当然还有其它的数据类型,这里没有用到而已,这也是就为什么要在开始声明std_logic_1164程序包的原因。

小小总结下:实体的整体框架如下所示:

entity 实体名 is

port ( 端口名: 端口模式(in/out) 数据类型);

end entity 实体名 ;

介绍完实体以后,下面介绍结构体部分,其实我们可以把实体想象为一个盒子,输入输出端口就是伸出盒子感知世界的触角,而下面要讲的结构体就是盒子的内部结构,也就是里面对于输入的变量做哪些处理再通过输出接口输入到外部。

这么讲是不是很明白了?

第三部分为:有关结构体(architecture)部分(包括进程(process))

architecture Func of Adder8 is

signal data :std_logic_vector(8 downto 0);

begin

process(in1,in2,cin)

begin

data <= (0 & in1) + (0 & in2) + ("00000000" & cin);

cout <= data(8);

pout <= data(7 downto 0);

end process;

end architecture Func ;

通过上面的例子也可以看出来,结构体也有固定的格式:以关键字architecture 开始,以关键字 end architecture 结束。其中Func为结构体名。

architecture 结构体名 of 实体名 is

结构体内使用到的变量、函数等声明;

begin

数据处理部分;

end architecture 结构体名 ;

其实,结构体就是实体的具体功能与行为描述,所以说每一个结构体都要与相应的实体对应起来,但是一个实体可以有多个结构体,但是一个结构体只能对应一个实体。毕竟一个结构体只能实现一个功能,但是一个实体可以很复杂有很多功能,这就需要很多结构体来实现。

begin 以前为结构体声明的部分,本例子中声明了一个信号类型的变量,data,数据类型为std_logic_vector ,位数为9位数。具体为什么要声明为9位在后面介绍。

signal data :std_logic_vector(8 downto 0);

begin 以后才为真正的实体行为功能描述,比如本实例中的process部分。主要实现8位加法器的具体功能,其中这里的process,是进程的意思,在一个结构体内可以有多个进程,并且各个进程之间是并行执行的,而进程内部是顺序执行的。比如下面的加法,先计算加法再将结果进行赋值。

process(in1,in2,cin)

begin

data <= (0 & in1) + (0 & in2) + ("00000000" & cin);

cout <= data(8);

pout <= data(7 downto 0);

end process;

其中process(敏感列表),括号内所谓的敏感列表,就是指可以触发该进程执行的变量,比如例程中的(int1,int2,cin)只要它们中的任何一个变量发生变化,该进程都会开始执行,即:开始执行加法运算。

data <= (0 & in1) + (0 & in2) + ("00000000" & cin);

在这行代码中,有一个字符“&”,估计很多同学会理解为 “与”运算,其实这里跟其它语言是不一样的,它表示“连接或者拼接”操作,举个例子:

比如 a=001,b=101 则 a&b=001101。

上面代码中的作用就是将in1,in2,cin 扩展为9位,但是不是随便扩展的,而是高位用0补充,这样实际上in1,in2,cin只有8位有效,但是得到的和是9位数,其中最高位data(8)为in1,in2相加以后的进位,这样就可以很容易的把进位与相加和分开。也就是下面分别赋值给两个输出端口。

cout <= data(8);

pout <= data(7 downto 0);

将进位赋值给输出端口cout ,因为加法完成以后是在data的最高位,data(8)。

将加法完成的和赋值给输出端口 pout。

其中“<=”是赋值符号,具体用法以后再讲,关于这个的使用,也有很多注意事项。

结束

谢谢您的阅读,有不对之处或者还不理解的可以留言讨论,一起学习。


注明:本内容来源网络,不用于商业使用,禁止转载,如有侵权,请来信到邮箱:429562386ⓐqq.com 或联系本站客服处理,感谢配合!

用户登陆

    未注册用户登录后会自动为您创建账号

提交留言