3.1 顺序语句
顺序语句是用来描述算法的语句,仅存在于process、function和procedure代码段中。在这些代码段中,顺序语句是逐行顺序执行的。
VHDL中的顺序语句包括if语句、case语句、wait语句、loop语句、null语句、信号赋值语句(见第2.1.3节)和变量赋值语句(见第2.1.5节)。
3.1.1 if语句
if语句是VHDL顺序语句的一种。根据一个或者多个条件,if语句选择相应的语句进行执行。计语句的语法结构如下。
其中,if_label是if语句的标签;condition是if语句选择执行代码段的判断条件;sequence_of_statements是与condition对应的执行语句。
例3.1 if语句示例
本例的代码嵌套使用4条if语句,实现异步复位的同步计数功能。
语句1的判断条件是判断低电平有效的复位信号rst_n是否有效。如果为真,将计数器的计数值和进位值清零;如果为假,进入后续操作。
语句2的判断条件是判断计数器时钟的上升沿是否到来。如果为真,进行后续操作。
语句3的判断条件是计数器使能信号是否有效。如果为真,计数器进行计数操作;如果为假,计数器的计数值保持不变。
语句4的判断条件是计数值是否达到最大值。如果为真,计数值清零,进位置高电平;如果为假,计数值自加一,进位置低电平。
3.1.2 case语句
case语句是VHDL顺序语句的一种。根据指定表达式的值,case语句从可选分支的语句段中选择其中一个分支进行执行。case语句的语法结构如下。
其中,case_label是case语句的标签;expression是case语句进入分支的判断表达式;choices构成表达式的取值列表;sequence_of_statements是与选项列表中choices对应分支的执行语句。取值列表必须涵盖表达式所有可能的取值,并且每一种取值只能出现一次。
choices可由多个choice构成,choice可由常量表达式、范围或者others表示。case语句需要在取值列表列出表达式的可能取值,这就需要用到others来表示所有未列出的情况。使用others表示其他情况时,others必须是最后一个choice。
例3.2 case语句示例
示例一中,根据x和y的值,case语句C1和C2分别对out_1和out_2赋值0、1、2或3。
示例二中,case语句C3在ex的值为num或num+1时,将op赋值为0;在ex的值为num+2时,将op赋值为1;在ex的值为num+3、num+4、num+5或num+6时,将op赋值为2;在ex的值为其他情况时,将op赋值为3。
3.1.3 比较if语句和case语句
if语句和case都是分支语句,可以在不同的条件下执行不同的操作,但两者之间还是有很大区别的。case语句必须将所有可能的分支都列出,而if语句则可以通过多层嵌套更加灵活地实现功能。
例3.3 4位优先级编码器
示例一中,if语句通过嵌套的方式实现优先级编码器。首先,检测data_in最高位是否为低电平。如果为真,直接将输出赋值为“00”;如果为假,检测次高位是否为低电平。接下来的操作以此类推。
示例二中,case语句实现优先级编码器。case语句直接将data_in作为表达式,列出data_in的所有可能情况,在每种情况后对data_out赋值。
示例一和示例二都实现了优先级编码器的功能,但采用if语句的示例一在代码量上要明显少于使用case语句的示例二。可以预见的是,如果分别使用if语句和case语句实现8位优先级编码器,两者的代码量将有很大的差别。
尽管if语句在代码展示上存在优先级,但是,在编写类似多路复用器的电路时,EDA工具在实际综合时会优化这部分电路设计。
例3.4 4选1多路复用器
示例一是通过if语句实现4选1多路复用器的代码。图0.1是示例一综合后的RTL电路。
图3.1 示例一综合后的RTL电路
示例一是通过case语句实现4选1多路复用器的代码。图3.2是示例二综合后的RTL电路。
图3.2 示例二综合后的RTL电路
比较图3.1和图3.2,不难看出,这两张RTL电路图是完全相同的。示例一和示例二综合后都是一个多路复用器模块,即使是使用了嵌套if语句的示例一也会被综合工具优化为多路复用器模块。
3.1.4 wait语句
wait语句是用来暂停process或者procedure顺序语句的。wait语句的语法结构如下。
其中,wait是wait语句的标签;signal_name是wait语句的敏感列表中的敏感信号,直到敏感列表中的其中一个信号发生变化,才开始执行后续操作;condition是wait语句需要满足的条件,指导满足condition的条件,才开始执行后续操作;time_expression是表示时间的表达式,直到等待time所确定的时间,才开始执行后续操作。
如果process中有wait语句,那么该process不可设置敏感列表。
例3.5 wait语句示例
示例一中,语句1在clk的上升沿时结束暂停,将a的值赋值给Q,进入语句2的暂停;语句2在10ns后结束暂停,将b的值赋值给Q,进入语句3的暂停;语句3在c发生变化时结束暂停,将c的值赋值给Q,进入语句4的暂停;语句4没有结束暂停的条件,该代码段进入永久暂停。
示例二中,首先将x赋值为高电平,进入wait产生的暂停;等到a或者b发生变化,并且clk为高电平时,结束wait产生的暂停,将信号x取反。
示例三中,wait语句是当enable为高电平时结束暂停,其效果与注释中的loop语句功能相同。
3.1.5 loop语句
loop语句是可以实现多次执行同一段语句的顺序语句。loop语句的语法结构如下。
其中,loop_label是loop语句的标签;iteration_scheme是loop语句循环的迭代方案;sequence_of_statements是需要循环执行的语句段。iteration_scheme的语法结构如下。
其中,iteration_scheme分为while和for两种结构。while结构中,condition是进入循环的前置条件,只有满足condition的条件,才能进入下一次循环;for结构中,identifier是循环参数的标识符,discrete_range是给定的离散范围,每一次循环结束时,该参数都会自加一,只有循环参数在离散范围内,才能进入下一次循环。在每一次循环中,循环参数可以看作常量,在循环过程中,不可以对循环参数进行赋值操作。
为了完善loop语句的功能,VHDL还定义了next和exit两个语句为loop语句进行补充。next语句实现跳出本次循环功能,如果还满足进入下一次循环的条件,循环会继续执行;exit语句实现结束循环的功能,无论是否满足进入下一次循环的条件,都会开始执行loop语句的下一条语句。
next语句的语法结构如下。
其中,next_label是next语句的标签;loop_label是所跳出循环的标签;condition是next语句可选的执行条件。
exit语句的语法结构如下。
其中,exit_label是exit语句的标签;loop_label是所结束循环的标签;condition是exit语句可选的执行条件。
例3.6 loop语句示例
示例一中,loop语句没有迭代方案。循环内容是延迟5ns将clk取反,实现周期为10ns的时钟信号输出。
示例二中,loop语句采用while格式的迭代方案。i是模拟的循环参数,与for格式的循环参数不同,此处的i可以进行赋值操作。一般来说,模拟的循环参数在每次循环过程中赋新的值,否则,该循环可能会永远无法结束循环,成为无限循环。此处,i在每次循环结束时自加一,当大于8时,不再进入下一次循环。
示例三中,loop语句采用for格式的迭代方案。循环参数i的初始值是1。当i在1至8的范围时,将不断进行循环;当i等于9时,将结束整个循环。
示例四中,语句1是结束整个L4的loop语句,不再进入下一次循环。语句2是在满足i与m相等时结束语句所在的loop语句,即L5的loop语句,结束后程序会进入L4的下一个循环。
示例五中,语句1是在满足countvalue与n相等时跳出所在loop语句的本次循环,即L7的本次循环。跳出本次循环后,如果循环参数在自加一后仍然满足迭代方案的条件,则继续进入下一次循环;否则,结束L7的loop语句。语句2是跳出L6的loop语句的本次循环。跳出本次循环后,继续进入下一次L6循环。此处的L6没有设置循环条件,也没有在循环内部模拟循环参数,或使用exit语句,该循环是一个无限循环。
3.1.6 null语句
null语句是执行空操作的语句,即语句不会对程序产生任何影响,直接跳转到下一条语句。null语句的语法结构如下。
其中,null_label是null语句的标签。
当程序对于某些分支中不需要执行任何操作时,可以使用null语句。case语句必须列出所有可能出现的分支,也就会出现不执行任何操作的分支,这时就可以使用null语句来作为分支执行的内容。
例3.7 null语句示例
示例中,当data_in为低电平时,不执行任何操作,即不修改data的值;当data_in为高电平时,将data赋值为“01”;当data_in为其他值时,将data赋值为“10”。