![人工智能硬件电路设计基础及应用](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=1738908422-nTY4JtE4S1eh2TzsIUhi6UBavo95pVQh-0-6b2a0fb0dab149baedc8a63915a8202f)
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_69_1.jpg?sign=1738908422-a3K756wcWEXMYSIGzUSYF9PXTRFd6jTj-0-1a14aa2c05063ca0e36843ca2cbf6af8)
其中,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=1738908422-CWTkNUYqqr54j7CEiNq8jfUtMOqYGMXV-0-9db06676d74aff20d195d104ba2ac43a)
3.1.2 case语句
case语句是VHDL顺序语句的一种。根据指定表达式的值,case语句从可选分支的语句段中选择其中一个分支进行执行。case语句的语法结构如下。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_70_1.jpg?sign=1738908422-ZuMebywRJytM8vaKPhrExLPXTF4es84y-0-83a0bc715aa257c3c7268c73cfa91fdc)
其中,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=1738908422-wtpcrTN3EuEyfIgKcFQ0U8viKDjOkKTo-0-b1c25b2be6e7c0409d93c946555845ae)
例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=1738908422-EZbVvNItWTm0CfAL68y0XGmenGVwBYjO-0-a524e11eb9b96d62df409059cd23b7a9)
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_71_1.jpg?sign=1738908422-TKpQRi94tn7I4acIjr6RRMPuUiTPHDg1-0-0f4f2466136824585bda4b34b4d9b663)
示例二中,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=1738908422-xwoEijZy0JXWu1vJ5H51Vd4OUp4qCZjU-0-e7d1135a274ac894d7ef134f66f9cd03)
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=1738908422-rBqFZ2BtsXLZkmFtt3ORF83cawmIy7x3-0-6477a060a7a2321554538f483cfbd5f3)
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_72_1.jpg?sign=1738908422-5ds18BxJJytlQBOhVV4FGMlIGwhbNtTn-0-f649d87d111866062650ae816c5331d4)
示例二中,case语句实现优先级编码器。case语句直接将data_in作为表达式,列出data_in的所有可能情况,在每种情况后对data_out赋值。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_72_2.jpg?sign=1738908422-u7wGdqsxjA8FbAkbmnXxLlgpeug29vXC-0-dc15f53e60c67f1cb50587ecaaf9412c)
示例一和示例二都实现了优先级编码器的功能,但采用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=1738908422-H12tb0OUF4USvzwaW6ArTmxpX7q4Vxo3-0-5d9c630509b146396f68de9ed10b9557)
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_73_2.jpg?sign=1738908422-syhKR619XdNf2IhAfTjDWcVFJZZpYwQZ-0-9ad86b7011d227916db1f17776a216f4)
图3.1 示例一综合后的RTL电路
示例一是通过case语句实现4选1多路复用器的代码。图3.2是示例二综合后的RTL电路。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_73_3.jpg?sign=1738908422-vDjo57dbowYoaiyJfRW8c5fOJe6QoE5w-0-cf09c1561f835a099d1b374d33f8b699)
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_74_1.jpg?sign=1738908422-NXgGkwXzbtGAV5CbBk8KWDXkTibDInsr-0-71ecd399e790487877899c2cac7cffb6)
图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=1738908422-YGOTzSpkpGPOsBAp93bDSDlmk8vxJShJ-0-58f56758b8450e5fe44e8e32c43f5da6)
其中,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=1738908422-SjXhs1n2rBSSwPZvqidZI6RjnXKvmvAJ-0-7e2f2354ec552da7e106d5d833e693e2)
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_75_1.jpg?sign=1738908422-b92fSEGlprCupAkCzUdqWcAh2j5Wsd5x-0-4cf1587afa5027aeddb16afd38ac4068)
示例二中,首先将x赋值为高电平,进入wait产生的暂停;等到a或者b发生变化,并且clk为高电平时,结束wait产生的暂停,将信号x取反。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_75_2.jpg?sign=1738908422-OcsD24VOoU34O8MIhW0XuiZ0GHFFr9SU-0-c7deb09771c751d94f7c6b3c8bf91d5d)
示例三中,wait语句是当enable为高电平时结束暂停,其效果与注释中的loop语句功能相同。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_75_3.jpg?sign=1738908422-mdSMdeE5ExkDpAxsCCw3O1HBX99QgXQT-0-9bb991124ed76860193eb04d9721817d)
3.1.5 loop语句
loop语句是可以实现多次执行同一段语句的顺序语句。loop语句的语法结构如下。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_75_4.jpg?sign=1738908422-JxXzegNY8lBQ2KWSDbNqOcFhSef7lbbu-0-f0176c90752826017f9814ea5dc3cca4)
其中,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=1738908422-HZPLyY9ZR2ar50DlCPvmKoW7GLwttQsh-0-883ea70f4d9619470543db2726a350f0)
其中,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=1738908422-H3CAGPVPiGYg9c2ly9NdGdotl6ApNtFL-0-5c1d92a658dbf11e5c41a3541799cc23)
其中,next_label是next语句的标签;loop_label是所跳出循环的标签;condition是next语句可选的执行条件。
exit语句的语法结构如下。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_76_2.jpg?sign=1738908422-r12ZoNerH6A83zVxlazZMXGBguMinFwa-0-f40beb3d6b0691551d837b0f9a2c87d7)
其中,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=1738908422-TNVUpzoRe8pjCQYs8eFBghGoOVQoaX2U-0-19af62a93c4e67519f9f31ce9493126b)
示例二中,loop语句采用while格式的迭代方案。i是模拟的循环参数,与for格式的循环参数不同,此处的i可以进行赋值操作。一般来说,模拟的循环参数在每次循环过程中赋新的值,否则,该循环可能会永远无法结束循环,成为无限循环。此处,i在每次循环结束时自加一,当大于8时,不再进入下一次循环。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_76_4.jpg?sign=1738908422-CGuWgrztula2l28h0fGL8RoeLvizWeFf-0-799f3960288003008de1c01ce5ce2ae5)
示例三中,loop语句采用for格式的迭代方案。循环参数i的初始值是1。当i在1至8的范围时,将不断进行循环;当i等于9时,将结束整个循环。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_77_1.jpg?sign=1738908422-kMDfQBldYlhg4TIVyDzjqZMRCn5ZQXdN-0-16f7c81239d4b744bc5d2d727cd26c30)
示例四中,语句1是结束整个L4的loop语句,不再进入下一次循环。语句2是在满足i与m相等时结束语句所在的loop语句,即L5的loop语句,结束后程序会进入L4的下一个循环。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_77_2.jpg?sign=1738908422-q0LTXoaANO0oqqYEEB42B35QK4yBsBUf-0-d7e701b93c8e6b52be89897908c7dcc8)
示例五中,语句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=1738908422-5mptOB1c32eInHq2EV6UwPgkeTTY7owW-0-380b0ed8e7f5c5a4cd4a112b669da210)
3.1.6 null语句
null语句是执行空操作的语句,即语句不会对程序产生任何影响,直接跳转到下一条语句。null语句的语法结构如下。
![](https://epubservercos.yuewen.com/B3251C/23020659009785406/epubprivate/OEBPS/Images/43035_78_1.jpg?sign=1738908422-AZ3SzilL2IDdDcTgyjLPoS1PVDOJTNP5-0-1388b156b701d2e72c290dae3d43c606)
其中,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=1738908422-TrQIiHmR3lNGe979hCSzj9V1TSl3MvE9-0-40179eecde71fc89b04df4fdc91ac2b5)