前段时间有人在网上问以下一段代码,代表什么意思。
always@(posedge clk ) begin
if( rst_n )
din <= 0;
else
begin
din <= din + 1’b1;
if( en )
din <= a – 1’b1;
end
初看这段代码,发现这里的din有两次进行赋值,首先是进行din的自加1操作,然后去判断en的值,为1的话然后将另外一个值减1在赋值给din,那最终的结果应该为什么了?
为此,特意写了一段代码,以及给代码的测试激励来测试输出结果为什么。
代码如下:
module ceshi
(
input clk,
input rst_n,
input [3:0] a,
input en,
output reg [3:0] b
);
always@( posedge clk ) begin
if( !rst_n )
b <= 3'd0;
else
begin
b <= b + 1'b1;
if( en )
b <= a;
end
end
endmodule
测试激励如下:
`timescale 1ns/1ps
module ceshi_tb;
reg clk;
reg rst_n;
reg [3:0] a;
reg en;
wire [3:0] b;
ceshi u1(
.clk(clk),
.rst_n(rst_n),
.a(a),
.en(en),
.b(b)
);
always #1 clk = ~clk;
initial begin
clk = 0;
rst_n = 0;
a = 3;
en = 0;
#20 rst_n = 1;
repeat(10)
begin
#30 en = 1;
#30 en = 0;
end
#100 $finish;
end
endmodule
用modelsim进行仿真。其仿真图如下所示:
从上图中进行分析
在复位信号为低电平,即复位信号有效。输出b的结果一直为0.
当复位信号没有效的时候,而且en为低电平,每当一个时钟的上升沿,输出b的值自加1一次。
当en为高电平的时候,输出b的值为输入a的值。。
其实,对于verilog中的always块中的信号赋值,如果采用的是非阻塞,也就是<=赋值。
每次<=赋值后,值不是立即更新,而是要等待所有的<=赋值结束后,才进行更新。而<=右边的值,都是最初始的值,不是计算后的值。
分析上面那段代码,在时钟的上升沿,
b <= b + 1'b1; b的值自加1,但是因为是<=赋值,因此b的值还没有改变,依然还是没有自加1时候的b。
if( en )
b <= a; 当判断en为1的时候,a的值就赋值给b,但是也不是立刻改变,要等所有always块中的<=赋值结束后,才会改变。
这个时候,我们就发现了矛盾,首先b的值自加1,但是同时再把a的值给b,这就给人一种双重赋值的感觉,因为verilog的语句是并行执行,两个都同时执行,那最后b的值为什么。
其实,这就要追朔到verilog这个语言对<=赋值的规定了。。。verilog规定,所有的<=赋值后,值不是立刻改变,而是要等所有的<=赋值后才进行改变,而对同一个变量,如果进行多次<=赋值,那最终的赋值结果以最后一个<=赋值结果为准。
所以说,对于上面那个代码,虽然对b进行了两次<=赋值操作,但是最后一个<=赋值是 b<=a,所以最后的结果是把a的值赋值给b,仿真结果说明了这点。
其实,上面那个代码功能其实是一个选择器,当en为1的时候,输出的值b等于输入的值a,否则输出的值b就自加1.
由上面代码,可总结到,对于同一个alywas块中的<=赋值,如果同一个信号进行多次<=赋值,那么结果,那个信号以最后一个赋值结果为准,也就是前面的赋值都是无效的。。
所以说,以下赋值:(假设初始a的值为0)
a < = a+1’b1;
a <= a +1’b1;
a <= a +1’b1;
**** (若干次对a赋值)
a <= a +1’b1;
最后a的值都是为1。也就是初始的a的值加1,也就是最后一句语句表达的。。。。其中间的赋值通通都被省略了。。。