数字调制解调技术的MATLAB与FPGA实现-设计语言及环境介绍 【1.7】

4.STD LOGIC ARITH 定义的数据类型STDLOGIC ARITH中主要定义了3种数据类型


        SMALLINT为INTEGER的子类型,只有“0”和“1”两种取值,在 VHDL设计中极少使用。有趣的是 UNSIGNED和SIGNED两种数据类型的定义方式,读到这里,你可能又觉得十分奇怪了,STDLOGICARITH中定义的两种数组类型UNSIGNED、SIGNED与STD LOGIC 1164中的 STD LOGIC VECTOR 的定义完全相同!这样定义有什么特殊的用处吗?当然,IEEE不会无聊到将一种数据类型用几个名字来定义而仅仅是为了多取几个别名而已。单单从字面上理解,STD LOGIC VECTOR 数据类型是指一组STDLOGIC类型的数据集合,SIGNED则指有符号数据,UNSIGNED指无符号数据。对于一组二进制数据来讲,我们有时要当成有符号数来运算,有时候要当成无符号数来运算,UNSIGNED与SIGNED类型数据正是为解决这一问题而定义的。

        ISE开发环境自动生成的VHDL源文件框架中有一条声明STDLOGIC UNSIGNED程序包的语句,而在源文件设计中,我们通常会将成组的二进制信号声明成STD LOGIC VECTOR 数据类型。这样,在设计中,操作数进行加、减、比较等操作时会当成无符号数运算。如果需要在设计文件中把操作数当做有符号数运算,则只需将或STDLOGIC UNSIGNED程序包改成STDLOGIC SIGNED即可。有读者可能要问了:如果在设计文件中要同时使用有符号数和无符号数操作的话,该怎么办呢?如果是这样,首先要在源文件头声明程序包STDLOGICUNSIGNED,然后在需要进行有符号数的操作时,用关键字 SIGNED指明操作数为有符号数。
        在STD LOGIC VECTOR 数据的前面加类型名UNSIGNED或SIGNED 可以看做数据类型转换,那么其他数据类型之间是否也可以进行这种方式的类型转换呢?案是否定的。STD LOGIC VECTOR与UNSIGNED、SIGNED之间可以进行类型转换的主要原因是它们的定义相同。在VHDL设计中,还有一种大家在其他语言中最为熟悉的数据类型INTEGER,虽然作者不主张在设计中用这种数据类型,但并非不能使用。如在设计中同时使用INTEGER与STDLOGIC VECTOR 两种数据类型,有时难免出现需要INTEGER与STDLOGICVECTOR进行类型转换的情况。庆幸的是IEEE已为我们编写好了类型转换函数,使用起来相当方便,读者可以查看STDLOGIC UNSIGNED程序包源文件来了解该函数的使用方法。

2.2.3数据对象

        VHDL语言的数据对象有3种--常量(constant)、变量(variable)和信号(signal)。常量的值在声明时即被指定,且设计中不能改变常量的值。常量的声明必须在VHDL文件中的声明区(关键字 architecture 与begin 之间)中声明;变量的值可以由 VHDL语句改变,变量只能在进程(process)或子程序之中声明,作用于变量的操作将立即改变变量的值;信号是 VHDL语言中独有的数据对象,也是最重要最普遍使用的数据对象,实体(entity)的端口必须是信号。信号只能在VHDL文件的声明区或实体的端口中声明,不能在进程或子程序中声明,信号的值可以由VHDL语句改变。信号和变量均可在声明时赋初值,关于信号与变量之间的区别在介绍VHDL语句时再详细阐述。下面是3种数据对象声明的例子。

2.2.4 运算符

        VHDL 的运算符可分为逻辑运算符(LogicalOperators)、关系运算符(RelationalOperators)、符号运算符(Sign Operators)、算术运算符(Arithmetic Operatorss)、移位运算符(Shift Operators)及连接运算符(ConcatenationOperators),每种运算符均在 VHDL的程序包中预先进行了定义。与其他编程语言一样,VHDL的运算符也存在重载的特性,即不同类型数据之间的运算操作采用相同的运算符时,程序可自动调用相应的运算符操作函数。

        VHDL 是一种类型检查十分严格的编程语言,所有不同数据类型的操作均需要有相应的重载运算符函数支撑,否则程序不能正确编译。
1.逻辑运算符
        逻辑运算符有7种:and(与)、or(或)、nand(与非)、nor(或非)、xor(异或)、xnor(同或)、not(非),每种运算符的意义与数字电路中门电路符号相同。图 2-1为几种逻辑运算符表达式综合后的 RTL原理图。

2.符号运算符
符号运算符有3种:负值符号()、正值符号(+)及取绝对值(abs)。由于正值符号(+)用于变量或信号前不改变原值,所以正值符号(+)几乎用不到。负值符号对原值取相反的值,绝对值运算即取原值的绝对值。由于负值符号及取绝对值均需用有符号数来表示,因此必须声明有符号数运算符号的程序包,如STDLOGICSIGNED。图2-2为STD LOGIC VECTOR类型数据的负值运算及取绝对值运算表达式综合后的RTL原理图:

        从RTL原理图中可以看出,负值运算是由取反及加法器2种基本逻辑元件完成的;而取绝对值运算则是由取反、加法器及多路信号选择器3种基本逻辑元件完成的。在应用符号运算符时,必须声明具有符号数运算符的程序包(STDLOGICSIGNED),否则程序不能编译通过,用 XST综合工具综合时,会在信息窗口中显示“abs(-)can not havesuch operands in this context.”(程序中不能有 abs(-)运算符)提示信息。
3.关系运算符
        VHDL 提供了6种关系运算符:等于(=)、不等于(/=)、大于(>)、小于(<)、大于或等于(>=)、小于或等于(<=)。各种关系运算符的意义也十分清楚,值得注意的是,STDLOGICSIGNED和STDLOGIC UNSIGNED 这两个设计中最常用的程序包中对6种关系运算符均进行了重载,使STDLOGICVECTOR 类型的数据可以直接与STDLOGIC VECTOR或INTEGER类型的数据进行比较,这给程序设计时代码的编写带来了不少的方便,因为一长串二进制数据的十进制值读写起来都不够方便。下面是一些关系运算符的语句例子。

        读者可能已经发现,在VHDL语言中符号“<=”“>=”可表示信号赋值,也可表示关系运算符,我们在写程序时是否需要刻意注意不同情况下的明确意义呢?完全不必要,且VHDL 语言也没有提供这项语法。在编译程序时,编译器会根据代码的上下文自动确定符号代表的含义。
4.算术运算符
        VHDL提供了7种关系运算符:加法(+)、减法(-)、乘法(*)、除法(/)、指数(**)求模(mod)、求余(rem)。但只有加法、减法、乘法三种运算符在程序包STDLOGICUNSIGNED和 STD LOGIC SIGNED中进行了定义,且这三种运算符的操作数及运算结果均可以是 STD LOGIC VECTOR 数据类型。之所以重点关心STDLOGIC VECTOR 类型的数据是否能直接使用各种运算符,是因为STDLOGICVECTOR是VHDL设计中使用最为普遍的数据类型。
        加法及减法运算在数字电路中实现相对较为简单,在用综合工具综合设计时,在RTL电路图中加、减操作会被直接综合成加法器或减法器元件。乘法运算在其他软件编程语言中实现起来十分简单,但用门电路、加法器、触发器等基本数字电路元件实现乘法功能却着实不是一件容易的事。用XILINX公司器件做FPGA/CPLD设计时,如果选用的目标器件内部集成了专用的硬件乘法器核(通常是18bitx18bit的IP核),则 VHDL语言的乘法运算符在综合成电路时将直接综合成硬件乘法器,否则综合成由LUT等基本元件组成的乘法电路,Technology原理图中可以更清楚地看到乘法器电路的组成结构。图2-3和图2-4分别是2位双输入加法器及乘法器的 Technology 原理图,目标器件为 XILINX 的 CPLD 器件 XC95108。

        除法、指数、求模、求余操作均没有在STDLOGICSIGNED和STDLOGICUNSIGNED程序包中定义,操作数及运算结果也没有STDLOGIC VECTOR 数据类型,因此无法在 VHDL 程序中直接对 STD LOGIC VECTOR 类型的数据进行相关运算。实际上,用基本逻辑元件构建这4种运算本身是十分繁杂的工作,如果要用VHDL实现这些运算,一种方法是使用开发环境提供的IP核或使用商业IP核,另一种方法只能是将算法分解成加、减、乘等操作步骤来逐步实现。

        XILINX的FPGA器件一般都提供除法器IP核。VHDL 语言中不仅 STDLOGICVECTOR 的数据类型不能直接使用这些运算,且设计文件中的变量及信号均不能使用这些运算符。那么 VHDL提供的这几种运算符在哪里使用呢?只在常量声明时使用,也即这些运算符只能对常量类型的数据进行运算。前面说过,常量本身就是一些固定值,直接指定即可,何必多此一举在常量表达式里加运算符?这还是为了代码书写的方便,以及在一些情况下更易表达常量的构成及常量之间的关系,如下面这些例子。
Constant RAM RAW: integer:=7;Constant RAM COL: integer:=8;
Constant RAM NUM: integer:=RAM RAW*RAM COLConstant EXP: integer:-9:Constant COUNT: integer:=2**EXP,
5.移位运算符
        NUMERIC STD程序包对4个移位运算符进行重载定义:逻辑左移(SLL)、逻辑右移(SRL)、循环左移(ROL)和循环右移(ROR)。在SLL及SRL运算时,移位后的空位填“0”。图2-5是4种移位运算操作的动作示意图。

5.移位运算符
NUMERIC STD程序包对4个移位运算符进行重载定义:逻辑左移(SLL)、逻辑右移(SRL)、循环左移(ROL)和循环右移(ROR)。在SLL及SRL运算时,移位后的空位填“0”。图2-5是4种移位运算操作的动作示意图。

        虽然NUMERICSTD程序定义了移位运算符,但该运算符只支持BITVECTOR类型的数据,所以在实际设计中极少使用。那我们在设计中要用到移位运算怎么办呢?STDLOGIC SIGNED和STD LOGIC UNSIGNED程序包已为我们定义好了两个移位函数-左移函数 SHL 及右移函数SHR。
fncton SHL(ARG:STD LOGIC VECTOR;COUNT: STD LOGIC VECTOR)return STD LOGICVECTOR;
function SHR(ARG:STD LOGIC VECTOR:COUNT: STD LOGIC VECTOR) reum STD LOGICVECTOR:
        上面两条函数定义语句在STDLOGICSIGNED及STD LOGIC UNSIGNED 程序包中定义。有关函数的内容在第3章中再详细阐述,从函数定义中可以看出支持我们需要的STDLOGIC VECTOR数据类型。由于SHL与SHR是函数,使用时需采用函数调用方法(参见下面的实例)。对于无符号数信号,SHL、SHR与SLL、SRL的作用相同:对于有符号数信号,SHL与SLL仍然相同,SHR运算时,右边的空位补原数据的最高位,也即补符号位,也称为符号扩展。

6.连接运算符
        连接运算符“&”用于将两个操作数连接起来,连接后的数据长度为两个操作数长度之和。连接运算符有什么作用呢?最直接的应用实例在于加法操作。我们知道,两个长为2比特的二进制数据相加,为了保证相加的结果不溢出,结果必须用长为3比特的数据保存,考察下面的例子。
signal a,b:std logic vector(3 downto 0);
signal c:std logic vector(4 downto 0);
c<=a+b;
程序综合时没有出现问题,但在用Modelsim做仿真时,却出现了问题
Error: Length of actual is 4. Length of expected is 5.
        原因还是 VHDL语言的严格的数据类型检查特点。连接运算符正好可以解决这一问题。对于无符号数来说,可在操作数的高位再连接1比特的“0”即可,对于有符号数来讲,在操作数的高位扩展1比特的符号位即可。在上面的例子中,将语句“c<=a+b”修改为“c<=('0’&a)+('0’&b)”后,程序可正常仿真。对于加法、减法操作来说,运算数据结果的长度与长度较长的操作数相同,也就是说前面的语句也可修改为“c<=('0’&a)+b”。

2.2.5 VHDL语句

        VHDL语言是描述硬件逻辑电路的语言,与其他软件语言有明显的区别。从描述的电路结构来看,VHDL有描述组合逻辑(CombinationalLogic)电路的语句和描述时序逻辑(SequentialLogic)电路的语句;从语句执行顺序来看,VHDL有并发语句和顺序语句两种。组合逻辑电路指没有触发信号的电路,电路输出结果直接由输入信号决定,当输入信号变化时,输出信号即发生变化;时序逻辑电路指有触发信号的电路,电路有记忆功能,电路的输出结果不仅受输入信号的影响,同时还受触发信号的控制,只有当触发信号及输入信号均发生变化时,输出信号才发生变化。组合逻辑电路与时序逻辑电路的含义与常规数字电路相同。读者应该还会记得,上一节讲解VHDL程序结构时介绍过语句体并行执行的概念。VHDL中的并发语句即指同时执行的语句,在编写代码时,并发语句无先后顺序之分,并发语句是写在 process(进程)之外的语句,这里的process也是VHDL的一个独特语句,本章将会详细介绍:顺序语句则是指按语句出现的先后顺序执行的语句,且顺序语句均需要写在 process之内。组合逻辑电路通常写在process之外,也可以写在process 之内。时序逻辑电路只能写在process之内。
        上面介绍的组合逻辑电路、时序逻辑电路、顺序执行语句、并行执行语句、process语句等概念之间的关系听起来可能会觉得有些混乱,因为它们是从不同角度来描述VHDL 语句的功能或特点的。读者在接下来对逐条VHDL语句的学习中,需要仔细体会各条语句的特点及所描述的功能电路模型。随着读者编程经验的增加,相信会很快对这些概念有更准确的把握。
1.赋值语句
VHDL有3种对象的赋值语句:常量赋值、变量赋值、信号赋值。

常量赋值语句是并发执行语句,只能出现在结构体的声明区,声明的常量仅在结构体设计内部可以引用。其语法为CONSTANT常量名:数据类型:数据或表达式:
        变量赋值语句是顺序执行语句,变量赋值语句只能综合成组合逻辑电路。变量可以先声明后赋值,也可以在声明时直接赋初值。变量的声明只能出现在process语块中的保留字process与 begin 之间,变量赋值语句只能出现在 process语句块之内,其作用域仅限于声明该变量的 process之内,也就是说一个 VHDL设计文件中除 process 之内有变量外,其他地方是不能有变量存在的。当程序执行到变量赋值语句时,变量立即改变成表达式的值。读者可能会觉得这种说法有些多余,难道还有不是立即改变赋值对象的语句吗?这也正是信号赋值语句与变量赋值语句的最大区别,也是硬件描述语言不同于其他计算机编程语言的最大特点。先来看看变量赋值语法,等介绍完信号赋值语句后再以一个实例讲述两者的差异。
variable变量名:数据类型:数据或表达式1;变量名:=数据或表达式;
        信号可以先声明后赋值,也可以在声明时直接赋初值。信号只能在结构体的声明区声明,不能在 process之内声明。信号赋值语句是顺序执行语句,也可以是并发执行语句。当信号赋值语句出现在 process之外时,为并发执行语句,只能综合成组合逻辑电路:出现在process 之内时为顺序执行语句,可以综合成组合逻辑电路或时序逻辑电路。当程序执行到信号赋值语句时,信号在一定时间后才改变成表达式的值。先来看看信号赋值语法。
signal信号名:数据类型:=数据]:信号名<=[transport]数据或表达式[after 时间];
        声明信号时赋初值以符号“:-”表示,此时程序执行当前语句时信号值立即改变;信号赋值符号为“<=”,即运算操作符中的“小于或等于”符号,当程序执行到当前语句时,信号在一定时间后才改变。用户可以通过保留字after显示地指定信号变化所需的延时,如果没有显示指定时间,则默认为 delta time的无穷短时间。需要注意的是 delta time 是比任何显示指定的时间都要短的时间。正是为了区分对象立即接受新值及延迟接收新值的概念,VHDL用两种不同的符号(:=、<=)来表示赋值操作。

  • 14
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BinaryStarXin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值