3.3 子程序
子程序是包含计算值或描述电路行为算法程序段,包含函数(function)和过程(procedure)两种形式,实现数据类型转换、以固定分辨率驱动信号等功能。子程序的使用包含两个重要的部分:子程序定义和子程序调用。子程序定义是对子程序具体功能的声明,子程序调用是执行定义中的程序段,实现特定功能。另外,顺序语句可以在子程序的内部使用,这是子程序的重要特征。
3.3.1 函数
函数是定义算法或者描述电路行为的子程序。函数可以看作返回特定数据类型的表达式,这是函数的一个重要特征。函数的返回类型可以是标量类型,也可以是复合类型。
函数的定义包含函数声明和函数主体两部分。函数声明包含了函数的部分信息,包括通用属性、参数列表、返回值。函数主体是函数定义的主要内容,除了包含以上信息,还对函数的具体行为或者算法进行了描述。
函数声明的语法结构如下。
其中,designator是函数的名称,可以是自定义的标识符,也可以是运算符;generic部分是函数的通用属性和通用属性映射;parameter部分是函数的参数列表,关键词parameter可以省略;return_identifier是函数返回值的标识符,type_mark是函数返回值的数据类型;在关键词function前添加impure可以将函数设置为非纯函数,添加pure或者省略可以将函数设置为纯函数。
函数主体的语法结构如下。
其中,function_declarative_part是函数的声明部分,包含子程序定义、包集声明、数据类型定义、变量定义等内容。function_statement_part是函数的语句部分,包含实现函数功能的语句段。
函数调用是函数执行的标志,此时,程序执行函数主体,将返回值作为函数的值继续进行其他运算和操作。函数调用的实质是表达式,因此可以出现在顺序语句或并行语句中。函数调用的语法结构如下。
其中,function_name是函数的名称;generic_map_aspect是通用属性映射;parameter_association_list是参数列表,关键词parameter map可省略。
在VHDL中,函数还可以分为纯函数和非纯函数。使用相同的参数列表调用纯函数,得到的返回值是固定的;而使用相同的参数列表调用非纯函数,得到的返回值是不固定的。
return语句是仅在子程序中使用的顺序语句,用来结束子程序返回到主程序。在函数中,return语句的语法结构如下。
其中,return_label是return语句的标签;expression是需要传递给主程序的返回值;condition是可设置的return语句触发条件。
例3.13 函数示例
示例一定义了两个and操作符函数,实现std_logic类型和bit类型的与操作。在函数一中,依据bit类型b的值设置与之对等的std_logic类型tmp,然后将a和tmp的与操作结果作为返回值返回给调用函数的主程序。函数二是函数一的重载,可以实现相同函数标识对不同参数列表执行不同操作。示例一中的函数二是针对bit类型参数为左操作数时的与操作,可以将参数对调后调用函数一作为函数二的返回值。
操作符函数调用有两种方式:使用操作符的字符串形式调用,按照“左操作数+操作符+右操作数形式”调用。
图3.8是与操作的函数示例仿真结果。从图3.8可以看出,outp1、outp2和outp3的波形是完全相同的,当且仅当ina和inb都为高电平时,与操作输出才为高电平。否则,输出低电平。
图3.8 与操作的函数示例仿真结果
示例二定义了两个4位unsigned类型的安全比较函数,完全比较两个参数后返回比较结果。安全比较函数的执行时间与参数的类型有关,与参数的值无关。
图3.9是安全比较的函数示例仿真结果。仿真结果中,inb的值为“0101”,ina的值一直在变化。当ina变化为“0101”时,outp输出为高电平;当ina继续变化为其他值时,outp输出为低电平。
图3.9 安全比较的函数示例仿真结果
3.3.2 过程
过程语句的作用与函数类似,但过程参数有输入和输出,比函数更为灵活。过程可以重复使用,相当于其他高级语言的子程序。
过程由过程名和参数表组成。参数表可以对常数、变量和信号这三类数据对象目标做出说明,并用关键词IN、OUT、INOUT定义这些参数的工作模式,即信息的流向。如果没有指定模式,则默认为IN。如果只定义了IN模式而未定义目标参量类型,则默认为常量。若定义了INOUT或OUT,则默认目标参量类型是变量。
过程声明的语句格式如下:
其中,designator是过程的名称,它始终是一个标识符;generic部分是过程的通用属性和通用属性映射;parameter部分是过程的参数列表,关键词parameter可以省略。
参数有常数、变量、信号,需明确说明,用关键字IN、OUT、INOUT定义参数信息流向,参数无特别说明,IN作为常数对待;只说明OUT、INOUT,作为变量对待,信号必须明确用关键字SIGNAL声明。数据类型只能是非限制形式,不能使用STD_LOGIC_VECTOR(0 TO 7)INTEGER RANGE 20 DOWNTO 0。
函数主体的语法结构如下。
其中,procedure_declarative_part是过程的声明部分,包含子程序定义、包集声明、数据类型定义、变量定义等内容,procedure_statement_part是过程的语句部分,包含实现过程功能的语句段。
过程调用就是执行一个给定名字和参数的过程。调用过程的语句格式如下:
括号中的实参表达式称为实参,它可以是一个具体的数值,也可以是一个标识符,是当前调用程序中过程形参的接受体。
形参名,即为当前欲调用的过程中已说明的参数名,即与实参表达式相联系的形参名。
return语句是仅在子程序中使用的顺序语句,用来结束子程序返回到主程序。在过程中,return语句的语法结构如下。
其中,return_label是return语句的标签;condition是可设置的return语句触发条件。过程可以返回多个值,也可以不返回值。
例3.14
在此示例中编写的是一个比较最大值的max过程。定义了两个4位STD_LOGIC_VECTOR类型的比较数。
上面示例中还显示了调用的部分,分别使用了并行过程调用和顺序过程调用。
顺序过程调用:
并行过程调用:
并行过程调用语句可以作为一个并行语句直接出现在结构体或块语句中。并行过程调用语句的功能等效于包含同一个过程调用语句的进程。
图3.10是max过程的示例仿真结果。仿真结果中,data1的值为“0101”,data3的值为“1101”。data2和data4的值在变化。当data2>data1/data4>data3时,也就是0~1ms的位置,输出out1/out2为data2/data4的值。当data2<data1/data4<data3时,也就是1~2ms时,输出out1/out2为data1/data3的值。其中,c、d、e为十六进制表达形式,对应的二进制分别为1100,1101,1110。
图3.10 max过程的示例仿真结果