FPGA探索(43)编写高效组合与时序逻辑

组合逻辑描述方法

范例介绍

如下范例中,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


注意事项

避免出现锁存
请注意,要想确保用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


请问上述代码描述的是不是一个组合逻辑电路呢?在【知己知彼篇->组合逻辑电路与时序逻辑电路】中,我们介绍过组合逻辑电路的概念,即**:如果数字电路满足任意时刻的输出仅仅取决于该时刻的输入,那么该数字电路为组合逻辑电路。** 根据此概念,我们来分析一下上述代码:上述例子中,输出为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


注意语句顺序

对于组合逻辑电路来说,是不存在反馈回路的(有反馈回路并且有意义的的组合逻辑电路其实就是时序逻辑,例如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


尽管颠倒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


注意事项

在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


也许你会反问,我们平时常说的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


清晰的时序逻辑描述方法

范例介绍

如下范例中,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;


无伤大雅的混写

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



-- 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


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

免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删

QR Code
微信扫一扫,欢迎咨询~

联系我们
武汉格发信息技术有限公司
湖北省武汉市经开区科技园西路6号103孵化器
电话:155-2731-8020 座机:027-59821821
邮件:tanzw@gofarlic.com
Copyright © 2023 Gofarsoft Co.,Ltd. 保留所有权利
遇到许可问题?该如何解决!?
评估许可证实际采购量? 
不清楚软件许可证使用数据? 
收到软件厂商律师函!?  
想要少购买点许可证,节省费用? 
收到软件厂商侵权通告!?  
有正版license,但许可证不够用,需要新购? 
联系方式 155-2731-8020
预留信息,一起解决您的问题
* 姓名:
* 手机:

* 公司名称:

姓名不为空

手机不正确

公司不为空