文章目录

  • 前言
  • VHDL的串行语句
    • VHDL直接信号赋值语句
    • VHDL变量赋值语句
    • VHDL条件语句
      • 优先级条件语句
      • 无优先级条件语句
      • 优先级条件语句与无优先级条件语句的对比
      • case-when的一些变形
    • VHDL空语句
    • VHDL循环语句
    • VHDL等待语句
    • VHDL过程调用语句

前言

所谓的串行语句,不过是形式上的串行,映射为硬件电路时仍然为并行的。

VHDL的串行语句

串行语句的执行方式是顺序执行的,一般高级编程语言中的语句执行方式都是顺序执行的,例如C语言,由此可见,顺序执行的语句更容易帮助我们来抽象和表达我们的设计思想,尤其是这样能够使时序逻辑的描述变得容易。所以,虽然FPGA的设计思路都是并行的,architecture中也仅支持并行语句的调用,但是为了方便设计者表达自己的思想,尤其是表达时序逻辑的思路,VHDL中的一些并行语句中的子语句体允许是顺序执行的,例如process。
除此以外,由于我们的代码是需要运行在PC机上的编译器来“读懂”并“翻译”成为数字电路的,而PC机上的运行的程序都是顺序执行的,所以,VHDL也必须支持串行的思路,否则编译器也无法工作。还有,仿真VHDL代码的工作也是在PC机上来执行的,所以VHDL本身也必须支持顺序的执行思路来达到仿真的目的。但是请注意,在VHDL中,串行语句的顺序执行只是一种帮助开发者、编译器、仿真器等理解代码、描述功能的一个辅助“思路”,而代码真正对应的硬件结构肯定是并行的!
到底VHDL里面有哪些串行语句可以供我们使用呢?以process为例描述如下:
process(<sensitive_list>)
begin
<VHDL直接信号赋值语句>;
<VHDL变量赋值语句>;
<VHDL条件语句>;
<VHDL空语句>;
<VHDL循环语句>;
<VHDL等待语句>;
<VHDL过程调用语句>;
end process;

VHDL直接信号赋值语句

直接信号赋值语句,也知道它是可以直接写在architecture中的并行语句,不过直接信号赋值语句具有双重身份,那就是它还可以当做串行语句来使用,例如:
process(a, b)
begin
c <= a and b; – 直接信号赋值语句
end process;

VHDL变量赋值语句

变量的特性就是赋值立即生效,由此也决定了它的执行结果必然跟语句的先后顺序有关,因此,变量赋值语句肯定是串行语句的一种。而变量的声明语句也是不能出现在architecture下的,而只能出现process、function、procedure等语句体的内部,所以变量的赋值语句也必然只能出现在这些语句体的内部。例如:

process(a, b)variable c : std_logic;
beginc := a and b; -- 变量赋值语句d <= c;
end process;

VHDL条件语句

条件语句是一种典型的串行语句。VHDL中共有两种条件语句——if-else和case-when,它们的根本区别是if-else中的各个条件分支是具有优先级的,且分支优先级按照书写顺序从高至低,而case-when语句是没有优先级的。关于它们的具体语法介绍如下:

优先级条件语句

优先级条件语句即if-else语句,它的完全语法如下:

if <expression1> then<statements>
elsif <expression2> then<statements>
<other elsif branchs>
else<statements>
end if;

其中的 elsif和else分支都不是必须的,可以根据具体情况来确定。举例如下:
– 求A、B、C三者中的最大值

if (A >= B and A >= C) thenmax <= A;
elsif (B >= C) thenmax <= B;
elsemax <= C;
end if;

注意,上例中,if、then(elsif、then)之间的条件判断表达式可以省略最外层的括号。
为什么说if-else语句是具有优先级的条件语句呢?需要从两个方面来说:

  • 第一,从语句的功能描述来分析。如果要描述上述求最大值的例子,我们可以这样翻译代码:首先,判断数A是不是大于等于B和C,如果成立,则最大值是A,结束判断;否则说明A不是最大值,那么这时候只需判断数B是不是大于等于C,如果成立,则最大值是B,判断结束;否则,由于之前已经得出A、B两数都不是最大值,那么最大值只能是C了。由此可见,每一个分支的判断都是建立在写在它之前的所有分支的基础上的。
  • 第二,从硬件实现上来说。上述求最大值的例子,对应到门级电路上,肯定是从A到max之间的路径最短,即所经过的逻辑门最少,而从B到max之间的路径次之,从C到max之间的路径最长。关于门级实现可以参考如下示意图:

    由此可见,基于优先级条件语句的特点,如果我们知道A、B、C三个数中最大值的概率是B大于C大于A,那么我们应该把对B的判断放在第一个分支,然后C放在第二个分支,而A放在最后一个分支。这样,今后的仿真效率会更高,且对于具体的FPGA实现,也能保证最短路径得到最充分的利用,这样芯片即使工作在比较恶劣的环境下,也能保证故障率达到最低。

无优先级条件语句

无优先级条件语句即是case-when语句,它的完全语法如下:

case (<expression>) iswhen <constant_value1> =><statements>;when <constant_value2> =><statements>; <other branchs>   when others =><statements>;
end case;

其中,的值必须互相不同,这和选择式信号赋值语句的要求是一样的。举例如下:
– 四选一多路选择器

case (sel) is -- sel is type of std_logic_vector(1 downto 0) when "00" =>data <= channel0;when "01" =>data <= channel1;when "10" =>data <= channel2;when "11" =>data <= channel3;when others =>data <= channel0;
end case;

注意,上例中,case后的判断表达式也可以省略括号。
上述例子中的分支已经覆盖完全,但是还是有一个when others分支,这虽然有些画蛇添足,但确是一个编程的好习惯,请大家注意!
为什么说case-when语句是无优先级的条件语句呢?也需要从两方面来说:

  • 第一,从语句的功能描述来分析。如果要描述上述多路选择器的例子,我们可以这样翻译代码:如果sel等于”00”,那么选择第一路输出;如果sel等于”01”,那么选择第二路输出;如果sel等于”10”,那么选择第三路输出;如果sel等于”11”那么选择第四路输出。可见这四个分支的判断之间没有任何相互关系,也互不为前提。
  • 第二,从硬件实现上来说。上述多路复用器的例子,对应到门级电路上,无论是channel0~3中的任何一个,到data的路径都是等长的。关于门级实现可以参考如下示意图:

    由此可见,在使用无优先级条件语句时,分支的顺序是无关的,不会影响电路的最终实现。

优先级条件语句与无优先级条件语句的对比

为了进一步说明if-else与case-when的不同,我们将之前用if-else编写的求最大值的例子用case-when重写如下:

x <= ‘1’ when A >= B else ‘0’;
y <= ‘1’ when A >= C else ‘0’;
z <= ‘1’ when B >= C else ‘0’;
judge <= x & y & z;
case (judge) is when "000" =>max <= C;when "001" =>max <= B;when "010" =>max <= A; -- unreachablewhen "011" =>max <= B;when "100" =>max <= C;when "101" =>max <= A; -- unreachablewhen "110" =>max <= A;when "111" =>max <= A;when others =><statement>;
end case;

关于其门级实现可参考如下电路图:

可见,此时,A、B、C到max的路径都是完全相等的。当然,由于上图并不是最简形式,所以此处我们没必要深究它与【优先级条件语句】小节中的例子到底孰优孰劣,但是请注意,由于目前的编译器都会对我们的代码有一定优化或修改作用,因此有时候if和case也可能会被综合成为一样的电路结构。

从上例可以看出,case结构中有很多分支输出结果完全一样,还有一些分支本身就不可达,例如,不可能A大于等于B,B大于等于C,然后A却小于C的。那么其实我们有时候为了书写方便,可以对case语句做一些变形。

case-when的一些变形

首先,利用特殊的“或”符号——“|”来简化代码,例如, 上述最大值无优先级条件语句可以变形为:

case (judge) is when "110" | "111" =>max <= A;when "001" | "011"=>max <= B;when "000" | "100" =>
max <= C;when others =>
max <= A;
end case;

其次,对于整数类型的表达式,还可以使用to关键字,例如:

case (number) is -- number is type integerwhen 0 to 100=>data <= A;when 101 to 200 =>data <= B;when others =>
data <= C;
end case;

第三,对于非整数类型的表达式,例如常用的std_logic_vector,可以通过类型转换函数先转成整数类型,然后再利用to关键字简化代码。
第四,切忌不可以利用std_logic的不关心态“-”来简化状态,例如,如果想当然的将上述最大值无优先级条件语句变形为:
– this is not right!

case (judge) is when "11-" =>max <= A;when "0-1" =>max <= B;when "-00" =>
max <= C;when others =>
max <= A;
end case;

虽然编译器不会报错,但是它的行为肯定是不对的,代码会始终停留在others分支。其原因是编译器对VHDL的不关心态“-”的理解跟我们预想的不一样。例如:
if (a = ‘-’) then
我们可能以为任何情况都能匹配,其实不然,目前为止的编译器还是把它理解为
if (FALSE) then
即,该条分支永远不可达。所以建议大家在平时的代码设计中尽量不要使用不关心态,如果非要使用,请参考下一点。
第五,不关心态在case中的应用。从第四点我们可以知道不关心态不能用于状态的简化,但是它的一个正确用法也是和case-when相关的,请看下面这个地址译码的例子:

case (address) is when "00" =>d <= "0001";when "01" =>d <= "0010";when "10" =>d <= "0100";when "11" =>d <= "1000";when others =>d <= "0000";
end case;

上例中,为了避免引入锁存,必须添加others分支,可是如果将others分支改为
d <= “----”;
那么会带来一定程度的逻辑电路简化。究其原因,是因为在本例的情况下,others分支本身就是我们不关心的分支,所以在此分支里面d到底等于多少都不影响程序的正常功能。可是如果强行给d赋一个确定的值"0000",那么编译器就会为不能匹配的address分支强行把d变为"0000"。可是也许此时让d等于"0010"更能简化我们的逻辑电路。所以,一个偷懒的方法就是给d赋值"----",告诉编译器我们不关心这个值,然后让编译器自己去优化这个分支的数字电路。所以,我们之前在讲逻辑数据类型时说过,不关心态“-”指的是我们不关心,而不是编译器不关心。

VHDL空语句

空语句本身不做任何的处理,仅仅是将程序的执行引导到下一个串行语句而已,它的语法如下:
null;
老实说,空语句其实没有什么作用,最多就是个占位符号而已。有些人习惯在条件语句的无处理动作分支中加入null语句,以达到列举完全所有分支的目的。不过这仅仅是你编写代码时列举出来了而已,只有null语句的分支对于编译器来说跟没有是一样的,所以如果用于组合逻辑还是会产生锁存器。

VHDL循环语句

VHDL中有两种循环语句,即for-loop语句和while-loop语句,语法分别为
for in <lower_limit> to <upper_limit> loop
;
end loop;

while loop
;
end loop;
关于循环语句,使用的时候有以下几点建议:
1、循环语句主要是起到简化代码书写和增加代码可读性的作用,此时,循环语句中控制循环次数的变量不会跟元件中的任何输入、输出端口或者信号量或者变量进行运算或比较,所以无论其它信号怎么变化都不会影响循环实现的功能,例如:
for i in 0 to 15 loop
a(i) <= b(15 - i);
end loop;

i <= 0;
while (i <= 15) loop
a(i) <= b(15 - i);
i <= i + 1;
end loop;
这样就省得我们写上16行代码,或者用一条长长的映射或连接赋值语句了,并且日后程序需求有变动时,修改起来也很容易。
2、循环语句的循环次数必须是有限的,因为循环语句是串行的,它必须依附于进程、过程或者函数语句,而进程本身就是一个不断循环的语句体,那么如果一个循环内部套了一个无限循环,那么显然,从代码上面我们就无法解释我们描述的到底是一个什么东西,并且从硬件上也得不到有意义的解。
3、循环语句通常不应该作为功能语句使用,因为所有的循环功能都可以通过进程的循环特性来描述和表示。而循环语句作为功能描述时的抽象层级过高,很难确定编译器到底会怎么理解。例如,可以接受的例子如下:
for i in 0 to 15 loop – 其实可以用一个针对t的case-when结构来实现
if(i < conv_integer(t))then – t is an input signal
c(i) <= b(15 - i);
else
c(i) <= b(i);
end if;
end loop;
而有问题的例子如下:
process(sel,b,c)
begin
while sel = ‘1’ loop
if(c = b)then
a <= b;
else
a <= c;
end if;
end loop;
end process;
首先,这段代码无法仿真,因为,一旦sel为’1’,那么循环将会一直运行,所以进程无法执行到末尾,从而也就无法响应sel、b、c的变化,从而即使外部的信号值已经改变,可是内部的代码仍然无法得到这个变化,因此代码将会无休止的在一个时刻点上被仿真器不停的调用同一个分支,从而产生死循环。
其次,这段代码的初衷可能是要完成这样的工作:
process(sel,b,c)
begin
if sel = ‘1’ then
if(c = b)then
a <= b;
else
a <= c;
end if;
end if;
end process;
只不过忘记了process的循环特性,才写成的while。
最后,建议大家主要还是用循环语句来做一些简化代码方面的工作,至于功能上的事情,完全可以交给process的循环机制来完成。

VHDL等待语句

VHDL中有四种等待语句,分别介绍如下:

wait;

表示永远挂起,用于仿真;

wait on <sensitive signal list>;

–信号发生变化将结束挂起,置于进程最下方代替进程开始的敏感量表,例如:

process
begina <= b and c;wait on b, c;        -- b, c任一信号发生变化时,进程重新开始
end process;
wait until <condition>;

条件表达式中的信号发生变化且满足条件时结束挂起,它可以用来描述触发、锁存等时序相关逻辑,例如:

process
beginwait until clk = '1';a <= b;wait until clk = '0';
end process;

它等效于

Process(clk)
BeginIf(clk'event and clk = '1')then
a <= b;end if;
end process;

不过通常情况下,还是建议大家使用第二种方式来描述时序逻辑,而对wait until语句仅作了解即可。

wait for <time expressions>;

超时等待语句,它主要用于仿真,等待够时间表达式中的时间后结束挂起。

VHDL过程调用语句

过程——procedure,也是VHDL子程序的一种过程的调用是一种串行的调用,它不可以直接出现在architecture的语句部分当中,一般需要依附于process等内部支持顺序执行的语句,例如:

 -- procedure min() is predefined  process(a,b)begin
min(a, b, c); -- a,b is input and c is the output
end process;

FPGA之道(29)VHDL的串行语句相关推荐

  1. FPGA之道(35)Verilog中的并行与串行语句

    文章目录 前言 Verilog的并行语句 Verilog连续赋值语句 普通连续赋值语句 条件连续赋值语句 Verilog程序块语句 沿事件 纯组合always 纯时序always 具有同步复位的alw ...

  2. FPGA数字信号处理(三)串行FIR滤波器Verilog设计

    该篇是FPGA数字信号处理的第三篇,选题为DSP系统中极其常用的FIR滤波器.本文将在上一篇"FPGA数字信号处理(二)并行FIR滤波器Verilog设计" https://blo ...

  3. FPGA之道(74)Verilog生成语句

    文章目录 前言 Verilog生成语句 循环生成 条件生成 generate-if语句 generate-case语句 前言 为什么要把这一节单独拎出来,因为个人原因,平时觉得用的Verilog生成语 ...

  4. FPGA之道(38)VHDL与Verilog的比较

    文章目录 前言 VHDL与Verilog的比较 语法比较 基本程序框架比较 端口定义比较 范围表示方法比较 元件调用与实例化比较 Process与always比较 标准逻辑类型比较 逻辑常量赋值比较 ...

  5. FPGA之道(30)编写自己的vhdl库文件

    文章目录 前言 编写自己的vhdl库文件 Work库 记录数据类型 子程序介绍 函数 过程 子程序使用总结 程序包 自定义程序包范例 前言 本文节选自<FPGA之道>来一起学习下高阶Ver ...

  6. FPGA之道(28)VHDL的并行语句

    文章目录 前言 VHDL的并行语句 VHDL直接信号赋值语句 VHDL条件式信号设置语句 VHDL选择式信号设置语句 VHDL进程语句 时钟事件表示方法 纯组合process 纯时序process 具 ...

  7. FPGA之道(22)VHDL基本程序框架

    文章目录 前言 VHDL基本程序框架 VHDL基本程序框架模板 Library Entity Architecture 声明与定义部分 语句部分 VHDL基本程序框架范例 VHDL注释语法 前言 VH ...

  8. FPGA之道(84)功能仿真之Verilog Test Fixture

    文章目录 前言 Verilog Test Fixture "Hello world"之Verilog Test Fixture 待仿真设计 仿真示例 示例详解 仿真结果 继承描述语 ...

  9. FPGA之道(71)提高设计的综合性能(三)提高设计的重用性与易改性

    文章目录 前言 提高设计的重用性 构建自己的IP库 提高设计的易改性 常量参数化模块设计 结构参数化模块设计 总线参数化 规模参数化 功能参数化 参数化设计的参数管理与组织 参数相关性 可传递的模块参 ...

最新文章

  1. 新兴AI解决方案将越来越依赖于嵌入式视觉技术
  2. 【译】BINDER - ANALYSIS AND EXPLOITATION OF CVE-2020-0041
  3. java中map可以为空吗_检查NavigableMap是否在Java中为空
  4. 使用firefox遇到的问题
  5. Git工作流指南:集中式工作流
  6. 【LeetCode】48. Rotate Image (2 solutions)
  7. Eclipse导入Android项目 Eclipse常见错误 中文乱码问题
  8. 召回2014年8英语个月的道路
  9. aiohttp+aiofiles异步爬虫光速下载图片
  10. jq 登陆界面 php,jquery实现用户登陆界面技术解答
  11. 如何编写爬虫获取淘宝网上所有的商品分类以及关键属性 销售属性 非关键属性数据
  12. CLH Lock 原理
  13. 戴尔笔记本电脑的计算机配置在哪,戴尔g3怎么查电脑配置
  14. JVM 双亲委派机制(通俗易懂)
  15. 你所不知的X86 CPU微码机制
  16. memcpy函数优化及DMA对比
  17. 奥利给!有了这么豪横的指南,还愁不会逛 GitHub?!
  18. CentOS 7 安装 libtool 2.4.6
  19. 《MATLAB》应用 之 用 MATLAB 将 视频 转换为 可调分辨率 的图片,badapple 视频转图片
  20. 阿龙的学习笔记---Effective C++---第一章:习惯C++

热门文章

  1. java -- 线程的生命周期
  2. 【笔记】C++ 简化位图图像操作
  3. 软件工程 之 动物世界
  4. FLEX是什么及与FLASH的关系的介绍
  5. linux不识别ntfs分区,WinUbuntu下linux无法挂载NTFS格式分区问题的解决
  6. linux 虚函数调用性能,C++虚函数和多态
  7. 列名无效怎么解决_电脑win键失效怎么办? 键盘win键无效的解决办法
  8. java thread join_java中thread的join方法为什么能让线程插队
  9. vba获取通达信光标的坐标数据_「高阶应用」谈一下VB6和VBA的坐标系统
  10. 基于pyBoard的进一步的一些测试实验,MC3008