![人工智能硬件电路设计基础及应用](https://wfqqreader-1252317822.image.myqcloud.com/cover/533/43738533/b_43738533.jpg)
3.1 顺序语句
顺序语句是用来描述算法的语句,仅存在于process、function和procedure代码段中。在这些代码段中,顺序语句是逐行顺序执行的。
VHDL中的顺序语句包括if语句、case语句、wait语句、loop语句、null语句、信号赋值语句(见第2.1.3节)和变量赋值语句(见第2.1.5节)。
3.1.1 if语句
if语句是VHDL顺序语句的一种。根据一个或者多个条件,if语句选择相应的语句进行执行。计语句的语法结构如下。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_68_1.jpg?sign=1739906897-hwR38IolnU0e7062d5GCQovz0q3e44xa-0-5d7c67a45e1bbd2b07fa4f3263f304ad)
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_69_1.jpg?sign=1739906897-HmUZ7SbOXFiqDgV5iAuBAU1e8Zop1Tle-0-3a004bb568ff062f7a92714e86e7e031)
其中,if_label是if语句的标签;condition是if语句选择执行代码段的判断条件;sequence_of_statements是与condition对应的执行语句。
例3.1 if语句示例
本例的代码嵌套使用4条if语句,实现异步复位的同步计数功能。
语句1的判断条件是判断低电平有效的复位信号rst_n是否有效。如果为真,将计数器的计数值和进位值清零;如果为假,进入后续操作。
语句2的判断条件是判断计数器时钟的上升沿是否到来。如果为真,进行后续操作。
语句3的判断条件是计数器使能信号是否有效。如果为真,计数器进行计数操作;如果为假,计数器的计数值保持不变。
语句4的判断条件是计数值是否达到最大值。如果为真,计数值清零,进位置高电平;如果为假,计数值自加一,进位置低电平。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_69_2.jpg?sign=1739906897-EVTDTlvzUUYrIgRDA3Tb9F7sk8NNcWgF-0-b3ba050101f7ad96f7159b06cdd7dc9b)
3.1.2 case语句
case语句是VHDL顺序语句的一种。根据指定表达式的值,case语句从可选分支的语句段中选择其中一个分支进行执行。case语句的语法结构如下。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_70_1.jpg?sign=1739906897-8UyxuDYy4OJZKmXLLV9O55mmzfaX80yF-0-8860084b9c84dea29c400187dd885a61)
其中,case_label是case语句的标签;expression是case语句进入分支的判断表达式;choices构成表达式的取值列表;sequence_of_statements是与选项列表中choices对应分支的执行语句。取值列表必须涵盖表达式所有可能的取值,并且每一种取值只能出现一次。
choices可由多个choice构成,choice可由常量表达式、范围或者others表示。case语句需要在取值列表列出表达式的可能取值,这就需要用到others来表示所有未列出的情况。使用others表示其他情况时,others必须是最后一个choice。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_70_2.jpg?sign=1739906897-l9AgaPLvvE7dC3GRqUDVIH7WCCacjqnW-0-586310fddda706c6c40fe5d9d1e717a7)
例3.2 case语句示例
示例一中,根据x和y的值,case语句C1和C2分别对out_1和out_2赋值0、1、2或3。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_70_3.jpg?sign=1739906897-HE75X40h077gf0IuTAXBTWjhf6rNdn1u-0-a3e521f2b3f4d1ede40503a2e1837def)
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_71_1.jpg?sign=1739906897-JG8YIRSanlOMhc4TRLgr7bvfltVhBiLO-0-64e1ca01ae37a9c274e305d0fb732402)
示例二中,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。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_71_2.jpg?sign=1739906897-l9drTEWxtBo14ybZzm0xlVcFGPbHWvMu-0-cc17dce16ea8501f4498b166cb67b130)
3.1.3 比较if语句和case语句
if语句和case都是分支语句,可以在不同的条件下执行不同的操作,但两者之间还是有很大区别的。case语句必须将所有可能的分支都列出,而if语句则可以通过多层嵌套更加灵活地实现功能。
例3.3 4位优先级编码器
示例一中,if语句通过嵌套的方式实现优先级编码器。首先,检测data_in最高位是否为低电平。如果为真,直接将输出赋值为“00”;如果为假,检测次高位是否为低电平。接下来的操作以此类推。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_71_3.jpg?sign=1739906897-Yg2wdchm4G8Su2cgyOcF4clAuUoqyS9M-0-410bbd126f1d149cd093fd92917831fa)
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_72_1.jpg?sign=1739906897-mmWmnCcUmVdEPuiIsm60mgiBHl4zmTvw-0-85cff8a68d556a940c7f380336473101)
示例二中,case语句实现优先级编码器。case语句直接将data_in作为表达式,列出data_in的所有可能情况,在每种情况后对data_out赋值。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_72_2.jpg?sign=1739906897-2sFh5PqACngpbl5UGiICEUunlnBHSRKc-0-b24012ea97b1e90cbb41a64d837ecea8)
示例一和示例二都实现了优先级编码器的功能,但采用if语句的示例一在代码量上要明显少于使用case语句的示例二。可以预见的是,如果分别使用if语句和case语句实现8位优先级编码器,两者的代码量将有很大的差别。
尽管if语句在代码展示上存在优先级,但是,在编写类似多路复用器的电路时,EDA工具在实际综合时会优化这部分电路设计。
例3.4 4选1多路复用器
示例一是通过if语句实现4选1多路复用器的代码。图0.1是示例一综合后的RTL电路。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_73_1.jpg?sign=1739906897-eTujtmF1TjNPB4tqLP6ys2U74noJBEiL-0-579ddbba16daf02a6d3f77dec1625abe)
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_73_2.jpg?sign=1739906897-ojnR3sXejUNM9lLST8snSAihu5tQUHbJ-0-000fba0ced6e56ac9d7fe56978120c1c)
图3.1 示例一综合后的RTL电路
示例一是通过case语句实现4选1多路复用器的代码。图3.2是示例二综合后的RTL电路。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_73_3.jpg?sign=1739906897-99MKwJ6oUUtPjHriTnfYlCKcyfaHnsgy-0-71a5754b9bccb949f311c03de7425246)
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_74_1.jpg?sign=1739906897-j9XpkuE0hnfRRIKymXgej3ftezRHHtVx-0-231a5a9ccdd146eb520d850601a3447b)
图3.2 示例二综合后的RTL电路
比较图3.1和图3.2,不难看出,这两张RTL电路图是完全相同的。示例一和示例二综合后都是一个多路复用器模块,即使是使用了嵌套if语句的示例一也会被综合工具优化为多路复用器模块。
3.1.4 wait语句
wait语句是用来暂停process或者procedure顺序语句的。wait语句的语法结构如下。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_74_2.jpg?sign=1739906897-yRIN4Bk1AXssuswGo4HSz7e0erYMOXJQ-0-69f89d784537e57cb099bc18aa69776a)
其中,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没有结束暂停的条件,该代码段进入永久暂停。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_74_3.jpg?sign=1739906897-BBTwvL4vvkaDUoEipc4aNXVQz2119OiF-0-e7fcc8a62b24f1bda2c2940376639100)
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_75_1.jpg?sign=1739906897-ebSP8UML0ml0HI7iEjp6B6ph4be3yok2-0-8f386d6ba5bdf6f2a6d9b80e72c2fec7)
示例二中,首先将x赋值为高电平,进入wait产生的暂停;等到a或者b发生变化,并且clk为高电平时,结束wait产生的暂停,将信号x取反。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_75_2.jpg?sign=1739906897-669X3aGfuz04PzL4ZwjoI449oyPXYh3D-0-3c516461f746ad7124b596e6f6acf667)
示例三中,wait语句是当enable为高电平时结束暂停,其效果与注释中的loop语句功能相同。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_75_3.jpg?sign=1739906897-ILC2eVgDbnS9uba5FmYXIzqGW7WmLbxt-0-8b40db579d5bc779cf189f3d9469a028)
3.1.5 loop语句
loop语句是可以实现多次执行同一段语句的顺序语句。loop语句的语法结构如下。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_75_4.jpg?sign=1739906897-XJEfNCODZmpLxkjfm6F12c34xL21pOBk-0-939df298e5d1bb3346a3e93eac967a9a)
其中,loop_label是loop语句的标签;iteration_scheme是loop语句循环的迭代方案;sequence_of_statements是需要循环执行的语句段。iteration_scheme的语法结构如下。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_75_5.jpg?sign=1739906897-zFFHJvalHjm0DdIIUVveye9p5DzYBZkd-0-d7968260888fbcdc1071c8600733042e)
其中,iteration_scheme分为while和for两种结构。while结构中,condition是进入循环的前置条件,只有满足condition的条件,才能进入下一次循环;for结构中,identifier是循环参数的标识符,discrete_range是给定的离散范围,每一次循环结束时,该参数都会自加一,只有循环参数在离散范围内,才能进入下一次循环。在每一次循环中,循环参数可以看作常量,在循环过程中,不可以对循环参数进行赋值操作。
为了完善loop语句的功能,VHDL还定义了next和exit两个语句为loop语句进行补充。next语句实现跳出本次循环功能,如果还满足进入下一次循环的条件,循环会继续执行;exit语句实现结束循环的功能,无论是否满足进入下一次循环的条件,都会开始执行loop语句的下一条语句。
next语句的语法结构如下。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_76_1.jpg?sign=1739906897-oZnR3HVcBSjcfthDoYs0qBJ3eMYup5ea-0-422fe29407f707837f255370e00e1227)
其中,next_label是next语句的标签;loop_label是所跳出循环的标签;condition是next语句可选的执行条件。
exit语句的语法结构如下。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_76_2.jpg?sign=1739906897-htaa4qWghjzV1uSabqsYHyPkPhK01BZB-0-f8a239b53e34a89d833fc178f4d9a5ea)
其中,exit_label是exit语句的标签;loop_label是所结束循环的标签;condition是exit语句可选的执行条件。
例3.6 loop语句示例
示例一中,loop语句没有迭代方案。循环内容是延迟5ns将clk取反,实现周期为10ns的时钟信号输出。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_76_3.jpg?sign=1739906897-3OkWgHiHpc66HfeOTbIowfOs0R2exti1-0-d46ce43ecb050c2710f557deabc2b138)
示例二中,loop语句采用while格式的迭代方案。i是模拟的循环参数,与for格式的循环参数不同,此处的i可以进行赋值操作。一般来说,模拟的循环参数在每次循环过程中赋新的值,否则,该循环可能会永远无法结束循环,成为无限循环。此处,i在每次循环结束时自加一,当大于8时,不再进入下一次循环。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_76_4.jpg?sign=1739906897-btdZoyeKMIyhfbBNOCjR3Fctsf5RMAG9-0-6afebb7acd4e1aec6f9b724e024c3663)
示例三中,loop语句采用for格式的迭代方案。循环参数i的初始值是1。当i在1至8的范围时,将不断进行循环;当i等于9时,将结束整个循环。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_77_1.jpg?sign=1739906897-gkwpHCJQmWEK8iRyq5K3W8vAiFWckX0F-0-89b9d3dd38f6078662c19e5f0d02725e)
示例四中,语句1是结束整个L4的loop语句,不再进入下一次循环。语句2是在满足i与m相等时结束语句所在的loop语句,即L5的loop语句,结束后程序会进入L4的下一个循环。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_77_2.jpg?sign=1739906897-DrK7mA854PiQiaei19x80u6yl6RytkZw-0-e45eb7f943cb1b1be811874d991b04bc)
示例五中,语句1是在满足countvalue与n相等时跳出所在loop语句的本次循环,即L7的本次循环。跳出本次循环后,如果循环参数在自加一后仍然满足迭代方案的条件,则继续进入下一次循环;否则,结束L7的loop语句。语句2是跳出L6的loop语句的本次循环。跳出本次循环后,继续进入下一次L6循环。此处的L6没有设置循环条件,也没有在循环内部模拟循环参数,或使用exit语句,该循环是一个无限循环。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_77_3.jpg?sign=1739906897-Ma282bCoaHWW2b9sKvo0u44NKrSxokCq-0-581d77282934a16dedc6195d8a8a27e2)
3.1.6 null语句
null语句是执行空操作的语句,即语句不会对程序产生任何影响,直接跳转到下一条语句。null语句的语法结构如下。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_78_1.jpg?sign=1739906897-ZaT2aUSX3QDqKhPkgnwNAeUpWgDfsZjO-0-e79a2882a94b51c7c2718ee38419fd60)
其中,null_label是null语句的标签。
当程序对于某些分支中不需要执行任何操作时,可以使用null语句。case语句必须列出所有可能出现的分支,也就会出现不执行任何操作的分支,这时就可以使用null语句来作为分支执行的内容。
例3.7 null语句示例
示例中,当data_in为低电平时,不执行任何操作,即不修改data的值;当data_in为高电平时,将data赋值为“01”;当data_in为其他值时,将data赋值为“10”。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_78_2.jpg?sign=1739906897-HR0VgSsqKRJBuXx03zroH6o3tAe7VWgT-0-1b301b9b93fbf82a5a0316d850e77929)