FPGA学习第六课 阻塞赋值与非阻塞赋值

  1. 阻塞/非阻塞赋值 概念
    https://www.cnblogs.com/xgcl-wei/p/9059789.html

  2. 本节主要内容

    FPGA实现变量赋值时,会由于器件原因造成延迟。举例来讲:
    我们要计算 o u t = a + b + c out=a+b+c out=a+b+c
    为了体现本节的效果,这里增加计算过程,首先定义一个中间变量 d d d,使得 d = a + b d=a+b d=a+b
    o u t out out就可以写成 o u t = d + c out=d+c out=d+c

其中 a 、 b 、 c a、b、c abc都是1位的数据,out最大值是3,因此设为2位的。tb仿真结果如下:
FPGA学习第六课 阻塞赋值与非阻塞赋值_第1张图片
通过添加子模块,可以查看变量 d d d(方法:在sim窗口中找到子模块名,右键add wave,然后restar一下modelsim,对他们分组即可,分组教程见上一课)。由于大家可以自己查看对应的波形图,我就不再贴很多中间过程的图了,这里只贴出来一个最后门级仿真的效果图。

FPGA学习第六课 阻塞赋值与非阻塞赋值_第2张图片
图上可见,很奇怪的是 o u t out out有一段时间出现了0!发生在 c c c跳转成0, b b b跳转成1的时候。
这是因为 o u t out out依赖于d,而这个过程由于电路延迟(此处以时钟上升沿为参照),d出现了0。导致输出也出现了0。这就是非阻塞赋值的一个问题。也就是计算结果和书写顺序没关系,导致中间会出现错误结果,宏观上看的确是正确的,但我们把信号拉到和时钟频率一个数量级的时候,就能发现错误了!

  1. 还是贴出代码
    block_and_noneblock.v
// 时序逻辑 输入必须有clk
// always@ 里面赋值的 必须为reg型

`timescale 1ns/1ns
`define tp 1

module block_and_noneblock(clk, rst_n, a, b, c, out);
	input clk;
	input rst_n;
	input a, b, c;
	
	output reg[1:0]out;  // out = a+b+c  最大是3   a:0/1
	
	// 定义一个中间变量d
	reg [1:0]d;            // 公式:d = a+b; 	// out = d+c;
	
	always@(posedge clk or negedge rst_n)
	if(!rst_n)  // rst_n == 0 复位
		out = 2'b0;
	
	else begin  // 这里面是并行执行的,但是顺序调换就不行
//		d = a + b;  // 阻塞赋值  与编写顺序相关
//		out = d + c;
	
//		out = d + c;  // 阻塞赋值  与编写顺序相关
//		d = a + b;
		
//		out <= d + c;  // 非阻塞赋值  与编写顺序无关  查看rtl 发现中间多一个寄存器
//		d <= a + b;
		
		d <= a + b; // #`tp 不会作用到硬件电路,只适用于仿真,模拟电路延迟
		out <= d + c;  // 非阻塞赋值 						 与编写顺序无关
	end
	
endmodule

block_and_noneblock_tb.v

`timescale 1ns/1ns
`define clock_period 20

module block_and_noneblock_tb;
	
	reg clk;
	reg rst_n;
	reg a, b, c;
	
	wire [1:0]out;
	
	block_and_noneblock block0(
		.clk(clk), 
		.rst_n(rst_n), 
		.a(a), 
		.b(b), 
		.c(c), 
		.out(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*100);
		a = 0; b = 0; c = 1;
		#(`clock_period*100);
		a = 0; b = 1; c = 0;
		#(`clock_period*100);
		a = 0; b = 1; c = 1;
		#(`clock_period*100);

		a = 1; b = 0; c = 0;
		#(`clock_period*100);
		a = 1; b = 0; c = 1;
		#(`clock_period*100);
		a = 1; b = 1; c = 0;
		#(`clock_period*100);
		a = 1; b = 1; c = 1;
		#(`clock_period*100)

		#(`clock_period*200);
		$stop;
	end

endmodule

  1. 总结与反思
    这一节弄了好长时间,遇到过以前的报错,同样的原因却没有第一时间发现。难受。

    最后,本菜鸟还有一点疑问:
    上面程序中,最后一图可以看出来out变量是在d变量更新后一段时间更新的,看上去不是依赖的clk上升沿,大佬们如果知道真相还望告知!

参考&致谢
小梅哥FPGA

你可能感兴趣的:(#,FPGA)