原创 2.我与FPGA的恋爱之赋值语句

2016-4-10 13:51 1524 22 23 分类: FPGA/CPLD 文集: 我与FPGA的恋爱

   Verilog HDL 语言中存在两种赋值语言:非阻塞型赋值语句,阻塞型赋值语句

           在Verilog HDL中阻塞赋值"="和非阻塞赋值"<="有着很大的不同.个人认为,作为初学者要掌握可综合风格的Verilog模块编程的8个原则,在综合布局布线的仿真中避免出现竞争冒险现象。(关于竞争冒险,后续会有单独章节记录)

  (1)  时序电路建模时,用非阻塞赋值。

  (2)  锁存器电路建模时,用非阻塞赋值。

  (3)  用always块建立组合逻辑模型时,用阻塞赋值。

  (4)  在同一个always块中建立时序和组合逻辑电路时,用非阻塞赋值。

  (5)  在同一个always块中不要既用非阻塞赋值又用阻塞赋值。

  (6)  不要在一个以上的always块中为同一个变量赋值。

  (7)  用$strobe系统任务来显示用非阻塞赋值的变量值。

  (8)  在赋值时不要使用 #0延时。

    用"="或者是"<="实际上对应的是不同的硬件电路

1. 非阻塞型语句
以赋值操作符“<=”来标识的赋值操作称为“非阻塞型过程赋值( NonblockingAssignment)”。 非阻塞型过程赋值语句的特点是:
( 1) 在 begin-end 串行语句块中,一条非阻塞过程语句的执行不会阻塞下一条语句的执行,也就是说在本条非阻塞型过程赋值语句对应的赋值操作执行完之前,下一条语句也可以开始执行。
( 2) 仿真过程在遇到非阻塞型过程赋值语句后首先计算其右端赋值表达式的值,然后等到仿真时间步结束时再将该计算结果赋值变量。也就是说,这种情况下的赋值操作是在同一仿真时刻上的其他普通操作结束后才得到执行的

 

下面看一下例程非阻塞赋值的代码和仿真:

module non_block(clk,rst_n,a,b,c,out);
	input clk;
	input rst_n;// _n低电平有效
	input a,b,c;
	output reg [1:0]out;	
	/*output reg的使用.reg是寄存器型变量的意思,一般在用时序逻辑编程时定义.而output代表此变量为输出。实际上两者并没有直接联系,但是如果这个变量既是时序逻辑(寄存器型)变量又要求输出,就既需要output,也需要reg...一般always@语句中赋值的变量都要声明为reg型*/
	reg [1:0] d;
	always@(posedge clk or negedge rst_n)
	if(!rst_n)
		out <= 2'b0;//三位数相加以1+1+1=3为例 转化为二进制 位宽两位
	else begin
		d <= a+b;
		out <= d + c;
	end	
endmodule

RTL视图:

2016-03-23_183855.jpg

仿真结果:

2016-03-23_184443.jpg

下面我们改变一下代码

	always@(posedge clk or negedge rst_n)
	if(!rst_n)
		out <= 2'b0;//三位数相加以1+1+1=3为例 转化为二进制 位宽两位
	else begin
		//d <= a+b;
		out <= d + c;//注意改变后的代码
		d <= a+b;
	end	

RTL视图:

2016-03-23_184038.jpg

从上面两张RTL视图中我们可以看到,在非阻塞赋值语句改变代码后,RTL结果不变

仿真结果:

2016-03-23_184443.jpg

 

2. 阻塞型语句
   以赋值操作符“ =”来标识的赋值操作称为“阻塞型过程赋值( blockingAssignment)”。非阻塞型过程赋值语句的特点是:
( 1) 串行块( begin-end)中的各条阻塞型过程赋值语句将以它们在顺序块后排
列次序依次得到执行。
( 2)阻塞型过程赋值语句的执行过程是:首先计算右端赋值表达式的值,然后立即将计算结果赋值给“ =”左端的被赋值变量。
  阻塞型过程赋值语句的这两个特点表明:仿真进程在遇到阻塞型过程赋值语句时将计算表达式的值并立即将其结果赋给等式左边的被赋值变量;在串行语句块中,下一条语句的执行会被本条阻塞型过程赋值语句所阻塞,只有在当前这
条阻塞型过程赋值语句所对应的赋值操作执行完后下一条语句才能开始执行。


下面看一下例程阻塞赋值的代码和仿真:

always@(posedge clk or negedge rst_n)
if(!rst_n)
    out <= 2'b0;//三位数相加以1+1+1=3为例 转化为二进制 位宽两位
else begin
    d = a+b;
    out = d + c;
end	

RTL视图:

2016-03-23_182626.jpg

仿真结果:

2016-03-23_185259.jpg

下面我们改变一下代码

if(!rst_n)
    out <= 2'b0;
else begin
    //d = a+b;
    out = d + c;//注意改变后的代码
    d = a+b;
end

RTL视图:

2016-03-23_183213.jpg

改变后与改变前的图明显不同,多出一个寄存器,我们可以看到,阻塞语句中最后生成结果与程序语句的先后顺序很重要的关联

仿真结果:

2016-03-23_185712.jpg

 

以上的仿真代码:

`timescale 1ns/1ns
`define clock_period 20   //定义时钟
module block_tb;
	reg clk;
	reg rst_n;
	reg a,b,c;

	wire [1:0]out;	
        
        block block0(.clk(clk),.rst_n(rst_n),.a(a),.b(b),.c(c),.out(out));
	//block block0(clk,rst_n,a,b,c,out);
	
	initial clk = 1;
	always#(`clock_period/2) clk = ~clk;
	
	initial begin
		rst_n = 1'b0;
		a = 0;
		b = 0;
		c = 0;
		#(`clock_period*200 + 1);
		rst_n = 1'b1;
		#(`clock_period*200);
		a = 0 ; b = 0 ; c = 0;
		#(`clock_period*200);
		a = 0 ; b = 0 ; c = 1;
		#(`clock_period*200);
		a = 0 ; b = 1 ; c = 0;
		#(`clock_period*200);
		a = 0 ; b = 1 ; c = 1;
		#(`clock_period*200);	
		a = 1 ; b = 0 ; c = 0;
		#(`clock_period*200);
		a = 1 ; b = 0 ; c = 1;
		#(`clock_period*200);
		a = 1 ; b = 1 ; c = 0;
		#(`clock_period*200);
		a = 1 ; b = 1 ; c = 1;
		#(`clock_period*200);
		#(`clock_period*200);
		$stop;	
	end
endmodule

注:例化中注释的部分是Augus失误所写成的代码,但是仿真结果全都都对,Augus当时像发现"新大陆"一样激动,在此解释一下,正确的例化:

block block0(.clk(clk),.rst_n(rst_n),.a(a),.b(b),.c(c),.out(out));
//block block0(clk,rst_n,a,b,c,out);

 

而我失误写的虽然对了,原因是该程序相对简单且端口较少,且默认来模块定义的接口来一一对应,例化(XXX)的顺序和定义的端口顺序相同,所以侥幸未出现失误

 

由于本人能力有限,出错之处的请各位给予帮助.谢谢!

PS:  前期多为整理(博主使用例程自己学习过程中的理解记录的笔记)

 更多资料参考

                发烧友小梅哥专版 http://bbs.elecfans.com/zhuti_fpga_1.html

                   梦翼师兄的炼狱传奇

文章评论1条评论)

登录后参与讨论

用户1447804 2016-4-12 14:52

整理筆記也是更好地學習與分享 :-)
相关推荐阅读
用户1867798 2016-05-25 18:13
[我和FPGA第一次接触]+我喜欢的姑娘那么优秀
 想起自己第一次接触FPGA,我感觉我当时就是一脸懵圈的给自己挖了一个大坑,最后只能用自己填的坑!想到一句话形容...西湖的水都是我的泪(当然夸张了哈~~~) 说起我和FPGA...
用户1867798 2016-05-23 20:30
关于Testbench的使用和Quartus中的模板
在这里主要总结一下关于我对Testbench的应用和Quartus软件中自带的模板使用 1.Testbench         关于testbench,其实就是一种验证的手段。首先,任何设...
用户1867798 2016-05-19 15:28
我与FPGA求交集之简易秒表计数实现
关于时钟计数器模块, 实现功能时钟计数: 毫秒,秒,分钟.....计数满清零 使用BCD计数,进位加一 按键控制(消抖模块),分频(时钟基...
用户1867798 2016-05-12 20:11
我与FPGA求交集之状态机中各种"码"的选择使用
         了解一下状态机中"状态"的各种编码,我们都知道每一个状态需要一个二进制表示,但是状态之间的关系如何,到底该选用"原码""Gray码""独热码"还是好多好多别的码呢.....^^ ...
用户1867798 2016-05-07 21:53
【软件技巧】Quartus中使用notepad++的关联设置
       我们应该都遇到过这种情况"同一个工程由于在不同版本上的Quartus自带的文本编辑器打开时会遇到中文注释注释乱码现象",有时候自带的编辑器不如一些专业的文本编辑器好用,在这里介绍的是...
用户1867798 2016-04-30 10:31
2.深入理解fpga应用设计之验证if_else的不同使用方式
验证: if_else不同使用方式,出来避免不受欢迎的意外产生的锁存器,同时还可以有效的利用逻辑资源, 修改:关于case,default......         例程实现方式一: ...
我要评论
1
22
关闭 站长推荐上一条 /2 下一条