FPGA之道(43)编写纯净的组合或时序逻辑

举报
李锐博恩 发表于 2021/07/15 02:54:20 2021/07/15
【摘要】 文章目录 前言编写纯净的组合或时序逻辑组合逻辑描述方法范例介绍注意事项注意语句顺序 纯时序逻辑描述方法范例介绍注意事项避免敏感双沿注意语句顺序 清晰的时序逻辑描述方法范例介绍无伤大雅的混写 前言 本文摘选自《FPGA之道》,一起看下作者对于组合以及时序逻辑编写的观点。 编写纯净的组合或时序逻辑 无论多么复杂的FPGA设计,如果我们将其中具有...

前言

本文摘选自《FPGA之道》,一起看下作者对于组合以及时序逻辑编写的观点。

编写纯净的组合或时序逻辑

无论多么复杂的FPGA设计,如果我们将其中具有存储功能的结构全部拿掉(寄存器、RAM、FIFO等等),那么剩下的若干独立数字电路网络则都是纯组合逻辑电路,对应的,我们称FPGA设计中这些具有存储功能的结构为纯时序逻辑电路。在这里,我们主要讨论纯时序逻辑电路由寄存器组成的情况,而其他存储结构可以类似来进行讨论。在数字电路的世界中,只有两种电路,即组合逻辑电路和时序逻辑电路,而时序逻辑电路中则包含了组合逻辑电路部分和纯时序逻辑电路部分。因此,当我们编写HDL代码的时候,我们所描述的不是纯组合逻辑电路就是纯时序逻辑电路。 那么,为了促进编译器对FPGA设计代码的理解,为了方便后期的代码维护、避免产生一些不必要的问题,建议大家在使用HDL进行FPGA设计描述的时候,尽量能够将对应组合逻辑的代码与对应纯时序逻辑的代码分离开来,先要做到自己清楚自己写的代码对应什么样的硬件电路。

组合逻辑描述方法

范例介绍

如下范例中,HDL代码所描述的电路就是纯粹的组合逻辑电路,对应逻辑与的功能。

-- VHDL example	
-- 并行信号赋值语句
a <= b and c;
-- 纯组合process语句
process(b, c)
begin
	a <= b and c;
end process; 

// Verilog example
// 连续赋值语句
assign a = b & c;
// 纯组合always程序块
always@(b, c)
begin
	a = b & c;
end

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

注意事项

避免出现锁存
请注意,要想确保用HDL描述的电路确实是组合逻辑电路,光靠模仿范例的写法还远远不够,请分析一下如下代码:

-- VHDL example	
process(sel, b)
begin
	if(sel = '1')then
		a = b;
	end if;
end process; 

// Verilog example
always@(sel, b)
begin
	if(sel = 1'b1)
		a = b;
end

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

请问上述代码描述的是不是一个组合逻辑电路呢?在【知己知彼篇->组合逻辑电路与时序逻辑电路】中,我们介绍过组合逻辑电路的概念,即**:如果数字电路满足任意时刻的输出仅仅取决于该时刻的输入,那么该数字电路为组合逻辑电路。** 根据此概念,我们来分析一下上述代码:上述例子中,输出为a,输入为sel和b。当sel为逻辑1时,输出a完全取决于b,可是当sel为逻辑0时呢?从HDL的语法上来分析,此时a应该保持最近一次sel为逻辑1时b的值,而与当前的b值无关。由此可见,上述代码并不符合组合逻辑的定义。
事实上,该段代码在综合的时候会引入锁存器,这是一个具有存储功能的单元,因此实际实现的是一个时序逻辑电路。而且锁存器还有很多缺点:首先,由于锁存器是毛刺敏感的,如果不能保证sel信号的质量,那么会造成输出信号a的不稳定;其次,FPGA芯片中一般没有锁存器这样一个资源,需要使用一个触发器加一些逻辑门来实现,比较浪费资源;第三,锁存器的引入会对时序分析造成困难。 因此,除非你现在就需要实现一个锁存功能,否则在编写组合逻辑时,请务必确认HDL代码覆盖到了组合逻辑的所有分支,以避免锁存器的引入。仍以上述代码为例,判断是否覆盖到了所有分支的一个方法,就是要确定无论输入sel和b怎么变化,process或者always中的代码从begin到end执行一遍,都能够经过至少一条对输出a的赋值语句。例如,修改正确后的代码类似如下:

-- VHDL example	
process(sel, b)
begin
	if(sel = '1')then
		a = b;
	else
		a = '0';
	end if;
end process; 

// Verilog example
always@(sel, b)
begin
	if(sel = 1'b1)
		a = b;
	else
		a = 1'b0;
end

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

注意语句顺序

对于组合逻辑电路来说,是不存在反馈回路的(有反馈回路并且有意义的的组合逻辑电路其实就是时序逻辑,例如RS锁存器)。所以电信号在组合逻辑电路中的传递方向是固定从输入端到输出端的,因此,为了增强代码的可阅读性,我们最好严格按照电信号的传递顺序来安排串行组合逻辑中的语句顺序 ,例如:

-- VHDL example	
process(a, b, c, d)
begin
	m0 <= a and b; 
	m1 <= c and d;
	dOut <= m0 and m1;
end process; 

// Verilog example
always@(a, b, c, d)
begin
	m0 = a & b; 
	m1 = c & d;
	dOut = m0 & m1;
end

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

尽管颠倒process或者always中的语句顺序,综合出来的电路结果是一样的,但是除了会影响代码的阅读性以外,还会对仿真产生许多不利影响。

纯时序逻辑描述方法

范例介绍

如下范例中,HDL代码所描述的电路就是纯时序逻辑电路,对应移位寄存器的功能。

-- VHDL example	
process(clk)
begin
	if (clk'event and clk = '1') then
		reg0 <= dIn;
		reg1 <= reg0;
		dOut <= reg1;
	end if;
end process; 

// Verilog example
always@(posedge clk)
begin
	reg0 <= dIn;
	reg1 <= reg0;
	dOut <= reg1;
end

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

注意事项

在FPGA中,我们主要针对寄存器来讨论时序逻辑的编写,因为绝大多数情况下我们是不会在FPGA中使用到类似锁存器的功能的,除此以外,像RAM、FIFO等存储单元,分析的方法与触发器类似。

避免敏感双沿

从范例中,我们可以看出,所谓纯时序逻辑,其实就是对寄存器的行为进行描述。其相对于组合逻辑最大的不同就是敏感时钟边沿事件,例如范例中描述的寄存器都是敏感时钟上升沿的,当然也可以是敏感下降沿的,这个需要根据实际情况来确定。
虽说纯时序逻辑最大的特点就是敏感时钟边沿事件,但是这也是有一定约束的。一个寄存器要么敏感时钟的上升沿,要么敏感时钟的下降沿,绝不可能同时敏感时钟的两种边沿事件,这是由其所对应触发器的结构和工作原理决定的。因此,类似如下的代码是无法进行综合的:

-- VHDL example	
process(clk) -- wrong example
begin
	if (clk'event) then
		a <= dIn; end if;
end process; 

// Verilog example
always@(posedge clk, negedge clk) // wrong example
begin
	a <= dIn;
end

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

也许你会反问,我们平时常说的DDR不就是在时钟的上升沿和下降沿同时传递和采样数据的么?没错,DDR的工作原理的确如此,不过这并不代表数字电路中就存在敏感双沿的触发器,事实上,DDR是通过若干敏感上升沿的触发器和若干敏感下降沿的触发器协作实现的。

注意语句顺序

老实说,在纯时序process中语句顺序的颠倒对设计功能以及仿真都不会有丝毫的影响,因为时序逻辑的赋值本身就是并行的。但是为了提高程序的可读性,还是建议大家尽量按照电信号的传递方向来组织代码顺序。 需要注意的是,纯时序逻辑中是可以出现反馈回路的,例如循环移位寄存器,此时可选取环路中的输入端作为代码起点、输出端作为代码终点进行编程,例如:

-- VHDL example	
process(clk)
begin
	if (clk'event and clk = '1') then
		if (load = '1') then reg0 <= dIn;
		else reg0 <= reg2;
		end if;
		reg1 <= reg0; reg2 <= reg1;
		dOut <= reg2;
	end if;
end process; 

// Verilog example
always@(posedge clk)
begin
	if (load = 1'b1)
		reg0 <= dIn;
	else
		reg0 <= reg2;
	reg1 <= reg0; 
	reg2 <= reg1;
	dOut <= reg2;
end

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

清晰的时序逻辑描述方法

范例介绍

如下范例中,HDL代码所描述的电路就是清晰的时序逻辑电路,对应计数器的功能。

-- VHDL example	
process(clk)
begin
	if (clk'event and clk = '1') then
		if (rst = '1') then count <= '0'; else count <= nextCount; end if;
	end if;
end process;

nextCount <= count + 1; 

// Verilog example
always@(posedge clk)
begin
	if(rst)
	begin
		count <= 1'b0;
	end
	else
	begin
		count <= nextCount;
	end
end

assign nextCount = count + 1'b1;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

无伤大雅的混写

上例之所以称之为清晰,是因为将描述组合逻辑与纯时序逻辑的代码分隔了开来,尤其是当代码所描述的功能稍微复杂一些的时候,这样更方便阅读和理解。下面我们来看一下,如果将上例的组合逻辑与纯时序逻辑合并到一起,会是什么样子,代码类似如下:

-- VHDL example	
process(clk)
begin
	if (clk'event and clk = '1') then
		if (rst = '1') then count <= '0'; else count <= count + 1; end if;
	end if;
end process;

// Verilog example
always@(posedge clk)
begin
	if(rst)
	begin
		count <= 1'b0;
	end
	else
	begin
		count <= count + 1'b1;
	end
end

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

通过对比可以看出,时序process或者always中,赋值操作符号的右边就是组合逻辑的藏身之处。当组合逻辑的功能比较简单时,这么做也是无伤大雅的,甚至能够令代码更加的简洁。

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

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

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

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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

举报
请填写举报理由
0/200