verilog-延迟语句

verilog 中的延迟语句可以分为:在赋值运算符的左侧延迟在赋值运算符的右侧延迟

#delay <LHS> = <RHS>;//左侧延迟

<LHS> = delay <RHS>;//右侧延迟

 左侧延迟,表示 赋值语句 在延迟到期后再执行,这是最常见的延迟控制形式:

运行结果:

波形如下:

在 5ns 的时候,a 和  c 切换为1,但是由于是 非阻塞赋值 ,所以在 5ns 的位置, a 和 c 的值并没有改变;如果此时将上面的代码改为:

#5  a <= 1;
    c <= 1;
    q <= a&c;

 波形如下:

此时在 5ns 的位置,由于 a 和 c 此时还是为0,所以q 还是保持为0不变,如果在6 ns 的位置再次求q:

#5  a <= 1;
    c <= 1;
	q <= a & c;
	#1  q <= a & c;

 则可以看到在 6ns 的位置,q 变为了1,波形如下:

扫描二维码关注公众号,回复: 14817117 查看本文章

 右侧延迟,表示 赋值语句 的右值首先进行计算,然后再延迟一定的时间,然后再给到左值;

运行结果和波形如下:

可以看到 q 始终为0,这是因为在 5ns 时,a c以及 第16行的右值同时被计算,然后右值再延时 15ns 再给到 q ,所以q 一直为0;

如果将上面改为阻塞赋值

#5  a = 1;
    c = 1;

    q <= #15 a & b | c;

 运行结果:

这时可以看到,q 在 20ns 的位置变为了 1,这是因为在 5ns 位置 a 和 c 立刻变为了1,并且去计算第16行的右值,此时 a=1 c=1 b=0,所以右值为1,然后延迟15 ns 给到 q;这就是阻塞赋值与非阻塞赋值的区别!!

下面将阻塞/非阻塞 和 左侧/右侧 延时来分别讨论,以加法器为例:

阻塞左侧延时

module addr(a, b, ci, co, sum);
	input [3:0] a, b;
	input ci;
	output reg co;
	output reg [3:0] sum;

	always @(*) begin
		#11 {co, sum} = a + b + ci;//
	end
endmodule

在第 21ns 时,a 变为10 ,此时启动 always 块,但是语句的执行要等到 11ns 之后,但是8ns 之后,也就是 29ns的位置,a 和 b 都发生了变化,所以在第32ns ,co 和 sum 的值为此时 a b ci 的最新值产生的结果,分别为 11 和 0;

如果改为使用中间变量,如下,仿真结果不变:

这是因为如果在第 21 ns ,a 发生变化,此时启动 always 块,但是要到 32 ns 的位置才会执行第10行,此时 tmp 为 a b ci 最新值的结果,同时将 tmp 赋给 co 和 sum;

如果中间变量改为如下:

波形为:

可以看到在21 ns 的位置a b 发生变化,启动 always 块,tmp 立刻变为 11,尽管在第29ns 的位置 a b 又发生了变化,但是被忽略了, sum 在第 32 ns 的位置赋值为 11,也就是说 第21 ~ 32 ns 之间 a b 的变化被忽略了;

由上面的例子可以得出:阻塞语句都是一条一条执行的,一条没有执行完绝对不会执行下一条!!不要将延时放在阻塞式赋值语句的左侧,这是一种不好的代码设计方式!!

 阻塞右侧延时

module addr(a, b, ci, co, sum);
	input [3:0] a, b;
	input ci;
	output reg co;
	output reg [3:0] sum;

	reg [4:0] tmp;

	always @(*) begin
		//tmp = a + b + ci;
		{co, sum} = #11 a + b + ci;//先计算右侧值,延迟11ns后再复制给左值
	end
endmodule

上面的效果和上面的左侧延时是一样的,即:

always @(*) begin
  tmp = a + b + ci; 
  #11 {co, sum} = tmp;
end

波形如下:

第21 ns a b 发生变化,然后直至32ns 才给到 sum ,中间 a b的变化被忽略,如第29ns;下面两种使用中间变量的结果和本次结果是一样的,都会忽略中间值,如下:

module addr(a, b, ci, co, sum);
	input [3:0] a, b;
	input ci;
	output reg co;
	output reg [3:0] sum;

	reg [4:0] tmp;

	always @(*) begin
		tmp = #11 a + b +ci;
		{co, sum} = tmp;
	end
endmodule

module addr(a, b, ci, co, sum);
	input [3:0] a, b;
	input ci;
	output reg co;
	output reg [3:0] sum;

	reg [4:0] tmp;

	always @(*) begin
		tmp = a + b +ci;
		{co, sum} = #11 tmp;
	end
endmodule

 

 由上面的例子可以得出:不要将延时放在阻塞式赋值语句的右侧,这是一种不好的代码设计方式!!

 非阻塞左侧延时

module addr(a, b, ci, co, sum);
	input [3:0] a, b;
	input ci;
	output reg co;
	output reg [3:0] sum;

	reg [4:0] tmp;

	always @(*) begin
		#11 {co, sum} <= a + b +ci;//左侧永远得到的都是最新值
	end
endmodule

第 21ns b 发生变化,然后延时 11ns 后,sum 的值为此时 a b ci 的最新值结果,而不是第21 ns 的值的结果;这个和第一个例子中的左侧阻塞延时 是一样的;

由上面的例子可以得出:不要将非阻塞延时放在赋值语句的左侧,这是一种不好的代码设计方式!!

 非阻塞右侧延时

module addr(a, b, ci, co, sum);
	input [3:0] a, b;
	input ci;
	output reg co;
	output reg [3:0] sum;

	reg [4:0] tmp;

	always @(*) begin
		{co, sum} <= #11 a + b +ci;//左侧永远都会跟随右侧的值变化而变化
	end
endmodule

 波形如下:

 

 可以看到,对于右侧 a b ci的任何一次变化,都会在 11ns 后反馈到 左侧co sum的变化中,如 M1 时刻反馈到 M2,M3 时刻反馈到 M4 ,并不会因为是在11ns 内的中间值变化而被忽略,这是非阻塞右侧延时 和 阻塞右侧延时 最大的区别!!!

由上面的例子可以得出:使用非阻塞右侧延时赋值,可以让左值随右值的变化而变化,建议使用 非阻塞右侧延时赋值!!!

再来看一下使用中间变量的情况,如下:

module addr(a, b, ci, co, sum);
	input [3:0] a, b;
	input ci;
	output reg co;
	output reg [3:0] sum;

	reg [4:0] tmp;

	always @(*) begin//注意敏感列表
		tmp <= a + b +ci;
		{co, sum} <= #11 tmp;
	end
endmodule

结果和上面一样,如下:

 连续赋值语句中的延时

连续赋值左侧延时

module addr(a, b, ci, co, sum);
	input [3:0] a, b;
	input ci;
	output reg co;
	output reg [3:0] sum;


	assign #5 {co, sum} = a + b +ci;
	
endmodule

波形如下:

可以看到,a 和 b 的变化会在 5ns 之后反馈到 sum 和 co 上,这个和上面使用 非阻塞右侧延时是一样的!!

module addr(a, b, ci, co, sum);
	input [3:0] a, b;
	input ci;
	output reg co;
	output reg [3:0] sum;


	assign #11 {co, sum} = a + b +ci;//改变左侧延时
	
endmodule

 改变左侧延时的大小到 11 ns,此时 a b 的变化周期为 8ns ,波形如下:

 出现 x 态的原因是因为,assign 赋值 要求左侧延时必须小于右侧信号变化周期的最小值,由于此时延时为11ns,所以导致 左侧 始终无法获得一个稳定值,所以保持为 x 态;

所以对于连续赋值左侧延时:

  • 左值会根据右值的变化而变化;
  • 左侧延时要小于 右侧信号 变化周期的最小值;
  • 等到右侧信号都不变时,再延时,然后输出计算结果给到左值;

 连续赋值右侧延时

module addr(a, b, ci, co, sum);
	input [3:0] a, b;
	input ci;
	output reg co;
	output reg [3:0] sum;


	assign {co, sum} = #5 a + b +ci;
	
endmodule

 结果会编译报错,如下:

这是因为右侧的线网类型没有记忆功能,所以在 assign 语句中给右侧加延时会产生语法错误!!

猜你喜欢

转载自blog.csdn.net/bleauchat/article/details/126861750
今日推荐