关于一段verilog代码的思考
0赞前段时间有人在网上问以下一段代码,代表什么意思。
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,也就是最后一句语句表达的。。。。其中间的赋值通通都被省略了。。。