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

举报
李锐博恩 发表于 2021/07/15 01:43:33 2021/07/15
【摘要】 文章目录 前言VHDL的并行语句VHDL直接信号赋值语句VHDL条件式信号设置语句VHDL选择式信号设置语句VHDL进程语句时钟事件表示方法纯组合process纯时序process具有同步复位的process具有异步复位的process具有混合复位的processVHDL块语句VHDL元件例化语句VHDL生成语句条件生成VHDL函数调用语句 前言...

前言

VHDL的并行语句类似于Verilog的always块,下面摘自《FPGA之道》的讲解,一起学习。

VHDL的并行语句

在VHDL的基本程序框架一节中,我们了解到,architecture语句部分里面的语句都是并行的。 那么到底VHDL里面有哪些并行语句可以供我们使用呢?请看如下描述:
architecture <arch_name> of <entity_name> is
begin
<VHDL直接信号赋值语句>;
<VHDL条件式信号设置语句>;
<VHDL选择式信号设置语句>;
<VHDL进程语句>;
<VHDL块语句>;
<VHDL元件例化语句>;
<VHDL生成语句>;
<VHDL函数调用语句>;
end <arch_name>;
以上这些并行语句,没有哪一类是不可或缺的,但是一个architecture中怎么着也得至少有一条,否则虽然从语法上来讲没什么问题,但是这个architecture就不具有任何功能了。一般来说,只要有元件例化、进程语句和信号赋值语句这三类语句就足够描述FPGA的功能了 ,下面详细介绍一下这些并行语句。

VHDL直接信号赋值语句

直接信号赋值语句语法如下:
<signal_name> <= ;
entity中的非输入端口以及architecture声明与定义部分中声明的signal,都可以在这个地方进行赋值。例如:

a <= (others => '0'); -- e.g. a is a signal, type std_logic_vector(3 downto 0);
b <= c or d; -- e.g. b, c, d are type std_logic;	

  
 
  • 1
  • 2

VHDL条件式信号设置语句

条件式信号设置语句语法如下:

  
 
  • 1
<signal_name> <= <expression 1> When <logic_expression1> Else <expression 2> When <logic_expression2> Else …… <expressions n>;

  
 
  • 1
  • 2
  • 3
  • 4
举例如下:

  
 
  • 1
equal <= '1' when data1 = data2 else '0';

  
 
  • 1
  • 2

VHDL选择式信号设置语句

除了条件式信号设置语句外,还有另一种语句可以实现类似的功能,那就是选择式信号设置语句。语法如下:

with <expression> select
<signal_name> <= <expression 1> When <constant_value 1> , <expression 2> When <constant_value 2>, …… <expression n> When others;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

举例如下:

	signal a, b : std_logic;
	signal c : std_logic_vector(1 downto 0);
	signal xorOfAB : std_logic;
	c <= a & b;
	With c Select
xorOfAB <= '1' when "01", '1' when "10", '0' when others;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

那么,条件式赋值语句和选择式赋值语句到底有什么区别和联系呢?首先,将上例用条件赋值语句改写如下:

xorOfAB <=0’ when a = b else1;

  
 
  • 1
  • 2

对比这两种写法,从表面上来看,选择式赋值语句的语法要求更为严格点,且constant_value的值必须互不相同,而条件式赋值语句的logic_expression却没有这种严格的要求。从本质上来看,条件式赋值语句之所以对logic_expression没有严格的要求,是因为它是有优先级的,即写在前面的logic_expression优先级高于写在后面的。
在VHDL基本程序框架一小节,我们说过,architecture中的独立语句其实就相当于只有一条代码的纯组合逻辑process。那么如果把条件式赋值语句和选择式赋值语句都改写为进程语句来实现的话,那么,条件式赋值语句就相当于if-else语法,而选择式赋值语句就相当于case语法。

VHDL进程语句

进程语句是VHDL中非常重要的一种语句体,在VHDL的基本程序框架一节中,我们已经有过简单介绍。Process是一种并行语句结构,位于一个architecture下的若干个process是并发执行的,但是Process内的语句却是顺序执行的顺序语句,process的语法结构如下:

<lable> : process (<sensitive_signals_list>) variable <name>: <type> := <value>; <other variables>...
begin <statements>;
end process;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

其中,是可选的,它是进程的标号,主要起到提高代码可读性的作用。之前已经介绍过,按照<sensitive_signals_list>的形式来分,process共有三个“纯种”的基本类型:纯组合process、纯同步时序逻辑process、具有异步复位的同步时序逻辑process。 不过如果从process的结构入手,我们的可以将process的语法划分可以更细一些,即:纯组合process、纯时序process、具有同步复位的process、具有异步复位的process以及具有混合复位的process。 可见,基于结构的划分比基于<sensitive_signals_list>的划分更细致一些、面更广一些。基于结构的划分其实就是基于时钟描述结构的划分,因此,首先来介绍一下在process中表示时钟事件的方法。

时钟事件表示方法

'event属性
首先要介绍一下’event属性,这个属性是VHDL进程语句中常用的描述信号事件的手段(进程语句最开始的敏感量表也是描述信号事件的手段,只不过它们所处的位置不同)。一般来说,时序逻辑主要响应两种时钟事件,即时钟的上升沿和下降沿。在process中,要表示这两种事件,我们可以采用如下代码:

	signal clk : std_logic;
	if (clk'event and clk = '1') then – 表示时钟上升沿
	if (clk'event and clk = '0') then – 表示时钟下降沿

rising_edge() & falling_edge()

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

除了’event属性之外,VHDL还提供了一种方法用来描述时钟事件,参考例子如下:

signal clk : std_logic;
if (rising_edge(clk)) then – 表示时钟上升沿
if (falling_edge(clk)) then – 表示时钟下降沿

切记!在一个process中,只能出现一个时钟信号的一种边沿事件,并且针对边沿事件的if(或elsif)条件分支,后续不得再有else或elsif分支存在。 这是由寄存器的结构决定的,因为一个寄存器只有一个时钟端口,并且只敏感这个端口的某一个边沿,因此凡是不尊重这个事实的代码都是不可综合的。并且,即便该寄存器存在异步复位信号,由于异步复位信号的优先级要高于时钟信号,而if语句是具有优先级的条件语句,故异步复位所处的条件分支也只能出现在时钟事件所处的分支之前(就像【具有异步复位的process】和【具有混合复位的process】中介绍的那样)。

下面根据时钟描述结构的不同,分别介绍五种基本process代码结构如下:

纯组合process

纯组合process语法如下:

process (<sensitive_signals_list>)
begin <statements>;
end process;

  
 
  • 1
  • 2
  • 3
  • 4

参考例子如下:
– e.g. all signals are std_logic

process(a, b)
begin
	c <= a and b;
	d <= not c;
end process;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

上述例子描述了一个与非门的结构,我们可以注意信号c并不在敏感量列表里面,因为c只是一个中间信号,而不是输入信号。

纯时序process

纯时序process的语法如下:

process (clk) -- only clk in the sensitive list
begin if (clk'event and clk = '1') then -- or if (clk'event and clk = '0') then <statements>; end if;
end process;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

参考例子如下:
– all signals are std_logic

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

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

具有同步复位的process

具有同步复位的process的语法如下:

process (clk) -- only clk in the sensitive list
begin if (clk’event and clk = '1') then -- or if (clk’event and clk = '0') then if (rst = '1') then -- or if (rst = '0') then <reset statements>; else <function statements>; end if; end if;
end process;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

参考例子如下:

-- all signals are std_logic
process(clk)
begin
if(clk'event and clk = '1') then	
if (rst = '1') then a <= '0';
else
a <= b and c;
end if;
end if;
	end process;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

具有异步复位的process

具有异步复位的process语法如下:

process (clk, aRst) -- clk and asynchronous reset
begin if (aRst = '1') then -- or if (aRst = '0') then <reset statements>; elsif (clk'event and clk = '1') then -- or elsif (clk'event and clk = '0') then <function statements>; end if; end if;
end process;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

也可以写成如下形式:

process (clk, aRst) -- clk and asynchronous reset
begin if (aRst = '1') then -- or if (aRst = '0') then <reset statements>; elsif (clk'event and clk = '1') then -- or elsif (clk'event and clk = '0') then <function statements>; end if;
end process;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

参考例子如下:
– all signals are std_logic

process(clk, aRst)
begin
if (aRst = '1') then a <= '0';
else
if(clk'event and clk = '1') then
a <= b and c;
end if;
end if;
	end process;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

具有混合复位的process

具有混合复位的process的语法如下:

process (clk, aRst) -- clk and asynchronous reset
begin if (aRst = '1') then -- or if (aRst = '0') then <asynchronous reset statements>; else if (clk'event and clk = '1') then -- or if (clk'event and clk = '0') then if (rst = '1') then -- or if (rst = '0') then < sync reset statements>; else <function statements>; end if; end if; end if;
end process;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

也可以写成如下形式:

process (clk, aRst) -- clk and asynchronous reset
begin if (aRst = '1') then -- or if (aRst = '0') then <reset statements>; elsif (clk'event and clk = '1') then -- or elsif (clk'event and clk = '0') then if (rst = '1') then -- or if (rst = '0') then < sync reset statements>; else <function statements>; end if; end if;
end process;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

参考例子如下:

-- all signals are std_logic
process(clk, aRst)
begin
if (aRst = '1') then a <= '0';
elsif(clk'event and clk = '1') then if (rst = '1') then a <= '1'; else
a <= b and c;
end if;
end if;
	end process;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

VHDL块语句

块语句即block语句,语法如下:
label:BLOCK <(expression)>

BEGIN
;
END BLOCK label;
关于block语句有几点说明:
首先,block中的语句都是并发执行的。
其次,关键字后紧跟的表达式逻辑是可选的,如果有表达式,那么当表达式为真时内部语句才执行;如果没有表达式,则语句一直执行。
第三,block中可以嵌套block。
最后,建议大家尽量不要用block语句。因为它的功能仅仅是对原有代码进行区域分割,增强整个代码的可读性和可维护性。当我们的设计真的需要分模块时,我们完全可以写多个entity,而没有必要用block;当我们需要让代码结构清晰时,我们完全可以用注释和空白,而没有必要用block。所以,建议大家对于block只做了解即可。

VHDL元件例化语句

元件例化语句即是instance语句。关于它的语法及相关知识已经在VHDL基本程序框架中做了比较详细的介绍.在这里,关于instance语句,我们再做两点说明:

  • 第一点,在某些情况下,例化元件时可以只对元件的部分端口进行映射赋值。道理很简单,对于那些你不需要使用的引脚,你可以不去管它(尤其是输出引脚,但输入引脚是否可以忽略不管则取决于功能),最形象的例子就是FIFO,关于空、满有非常多的信号,而我们的设计中一般只能用到其中的一部分。
  • 第二点,元件例化的时候,映射给输入端口的值也可以是常数或者非常简单的表达式,例如:
	-- all signals are of type std_logic_vector(3 downto 0)
m0: And3 
	PORT MAP(
		a => "0000",
		b => not b, -- this is ok!
		c => a and c, -- this is not allowed!
		d => d
	);

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

使用简单的表达式进行映射,有时候能够给我们带来一些便利,不过并不提倡大家这么去做,因为这样写有些不太规范。

VHDL生成语句

VHDL中的生成语句可分为三种,分别是参数生成、循环生成与条件生成,我们这里所说的并行语句主要指后两种。关于这三种生成语句分别介绍如下:

  • 参数生成
    参数生成的语法是generic,主要用在entity定义和元件例化时。

  • 循环生成
    循环生成也叫for-generate语句,主要用于生成一组信号赋值或元件例化语句。它的语法如下:

<generate_LABEL>:
for <name> in <lower_limit> to <upper_limit> generate
begin
<statements>;
end generate;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

关于循环生成语句的语法,有一点需要注意,那就是<generate_LABEL>必须要有,也就是说一定要为循环生成语句定义一个标签,否则会被识别为for语句,而for语句是串行语句,不能单独出现在architecture中。
先看一个循环生成语句在信号赋值方面的例子:

	signal a, b : std_logic_vector(99 downto 0);
invertVector:
for i in 0 to 99 generate
begin
a(i) <= b(99 - i);
end generate;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

由此可见,通过使用循环生成语句可以非常方便的实现a、b逻辑向量间的反转赋值,这样我们就不用写上100行啰嗦的直接信号赋值语句了。当然,这样的功能也可以通过在process中调用for循环语句来实现,不过下面这个利用循环生成语句进行元件例化的例子,就不那么容易找到同样简洁的替代方案了,代码入下:

COMPONENT cellAdd
PORT(
		a : IN std_logic_vector(7 downto 0);
		b : IN std_logic_vector(7 downto 0); c : OUT std_logic_vector(7 downto 0)
		);
	END COMPONENT;
type rt is array (7 downto 0) of std_logic_vector (7 downto 0);
signal arrayA, arrayB, arrayC: rt;
	adderGen : for i in 7 downto 0 generate begin arrayAdder: cellAdd Port map(
a => arrayA(i),
b => arrayB(i), 
c => arrayC(i));
end generate;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

可见,本例的方式也极大的简化了我们的代码编写,并且这种代码节省更加不容易被替代,除非从cellAdd上下功夫,但是这样会复杂基本模块,不可取。

条件生成

条件生成语句也叫if-generate语句,主要用于一些预编译前的代码控制,类似于C语言中的条件选择宏定义,根据一些初始参数来决定载入哪部分代码来进行编译(即,有选择的让部分代码生效)。例如,我们有时候不确定这个地方是需要一个乘法还是一个除法,可是如果同时实现一个乘法器和一个除法器,那么资源的占用会很多,那么这个时候,可以通过条件生成语句,来方便的在乘法器与除法器之间切换,并且只占用一份资源。注意!这种切换不是动态的,它是通过修改设计中的参数来实现的,所以需要重新编译。if-generate语句的语法如下:

< generate_LABEL >:
if <condition> generate
begin
<statements>;
end generate;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

举例如下:

constant sel : integer := 0;
signal a, b, c: std_logic_vector(15 downto 0);

myMul: if sel = 1 generate begin
c <= a * b; end generate;
myDiv: if sel = 0 generate begin c <= a / b; end generate;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

关于上例有四点需要注意:一、必须确保两个条件生成语句不能同时有效,否则就会出现c变量的赋值冲突错误。二、条件生成语句不支持else分支。三、判断条件必须为静态条件。四、标签不能省略。
请大家仔细思考第三点注意,绝对不能利用条件分支做一些动态生成的事情,因为硬件不像软件,不支持对象的动态生成与释放。条件生成语句是为了辅助编译器来预先知道到底该加载哪些语句去进行编译、综合的,而不能根据变量来在编译后动态决定现在选择哪一个代码结构。例如,如果把上例的
constant sel : integer := 0;
改为
signal sel : integer := 0;
那么编译会报错,表示判断条件不是一个静态条件。如果真的需要根据条件来判断做乘法还是做除法,应该写成这样:
signal sel : integer := 0;
cMul <= a * b;
cDiv <= a / b;
c <= cMul when sel = ‘1’ else
cDiv;
但是注意,这个时候,在FPGA中可是需要调用相应资源来同时实现一个乘法器与一个除法器的,只不过会根据sel来判断此时需要的是乘法器还是除法器的输出。而如果用条件生成语句的话,每一次实现FPGA的设计中,只会有一个乘法器或者一个加法器,资源方面会有很多节省,不过带来的不便就是如果需要另外一种功能时,需要修改代码重新编译实现新的FPGA配置文件。
需要强调一下,以上例子中,我们为了说明方便和代码简洁,直接使用了“*”、“/”符号来完成乘法与除法运算。在现实情况中,强烈不推荐如此简单的来处理乘、除运算.

VHDL函数调用语句

函数——function,是VHDL子程序的一种,函数的调用是一种并行的调用,它可以直接出现在architecture的语句部分当中,例如:
– e.g. function min() is predefined

smaller <= min(a, b);

  
 
  • 1

文章来源: reborn.blog.csdn.net,作者:李锐博恩,版权归原作者所有,如需转载,请联系作者。

原文链接:reborn.blog.csdn.net/article/details/104289103

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。