Verilog中的阻塞与非阻塞
按说阻塞与非阻塞是Verilog中最基本的东西,也是老生常谈。但是最近看到很多程序里用到阻塞语句竟然不是很明白,说到底是从来没有自己仔细分析过。当然一般情况程序中也是推荐用非阻塞的。
一般来说大家都会用以下几个例子来说明阻塞与非阻塞:
大家可以参考http://www.cnblogs.com/crazybingo/archive/2012/03/20/2408980.html
module Shifter1( |
|
module Shifter2( |
|
module Shifter3( |
|
module Shifter4( |
|
module Shifter5( |
|
module Shifter6( |
|
从上面的例子可以看出:
(1)阻塞语句定义的寄存器是可能被优化掉的
(2)阻塞语句定义的寄存器是否被优化是与语句顺序有关系的
参考文档中提到非阻塞语句执行的过程中先把每条语句当作一个事件放入事件队列中来执行的。它能很方便帮助理解非阻塞过程。
所谓非阻塞赋值,顾名思义,就是指当前语句的执行不会阻塞下一语句的执行。
always @(posedge Clk)
begin
Q1 <= D;
Q2 <= Q1;
Q3 <= Q2;
end
首先执行Q1 <= D,产生一个更新事件,将D的当前值赋给Q1,但是这个赋值过程并没有立刻执行,而是在事件队列中处于等待状态。
然后执行Q2 <= Q1,同样产生一个更新事件,将Q1的当前值(注意上一语句中将D值赋给Q1的过程并没有完成,Q1还是旧值)赋给Q2,这个赋值事件也将在事件队列中处于等待状态。
再执行Q3 <= Q2,产生一个更新事件,将Q2的当前值赋给Q3,这个赋值事件也将在事件队列中等待执行。
这时always语句块执行完成,开始对下一个Clk上升沿敏感。
那么什么时候才执行那3个在事件队列中等待的事件呢?只有当当前仿真时间内的所有活跃事件和非活跃事件都执行完成后,才开始执行这些非阻塞赋值的更新事件。这样就相当于将D、Q1和Q2的值同时赋给了Q1、Q2和Q3。
下面是参考中的例子及阻塞实现过程:
这里有一个数组:Data[0]、Data[1]、Data[2]和Data[3],它们都是4比特的数据。我们需要在它们当中找到一个最小的数据,同时将该数据的索引输出到LidMin中,这个算法有点类似于“冒泡排序”的过程,而且需要在一个时钟周期内完成。例如,如果这4个数据中 Data[2]最小,那么LidMin的值则为2。
module Bubble_Up( Rst_n, Clk, Data0, Data1, Data2, Data3, Lid_Min ); input Rst_n; input Clk; input [3:0] Data0; input [3:0] Data1; input [3:0] Data2; input [3:0] Data3; reg [3:0] Data [3:0]; output [1:0] Lid_Min; reg [1:0] Lid_Min; always @( posedge Clk ) begin Data[0] <= Data0; Data[1] <= Data1; Data[2] <= Data2; Data[3] <= Data3; end always @(posedge Clk ) begin if (Data[0] <= Data[Lid_Min]) //"<="表示小于等于 begin Lid_Min = 2'd0; //"<="表示非阻塞赋值 end if (Data[1] <= Data[Lid_Min]) begin Lid_Min = 2'd1; end if (Data[2] <= Data[Lid_Min]) begin Lid_Min = 2'd2; end if (Data[3] <= Data[Lid_Min]) begin Lid_Min = 2'd3; end end endmodule
从实现的RTL视图上,我突然觉得阻塞一步到位的完成了很多组合逻辑。接下来我再说一些在程序上经常会遇到的例子。
reg current_state;
reg next_state;
reg inc_loops;
reg clr_loops;
reg set_valid;
reg [4-1:0] loops;
reg [23:0] threshold;
reg [28-1:0] dim_cnt_thresh_r;
reg [28-1:0] cnt_thresh;
always @ (posedge clk )
begin
current_state <= next_state;
end
always @ (*)
begin
next_state = current_state;
clr_loops = 1'b0;
inc_loops = 1'b0;
set_valid = 1'b0;
case (current_state)
0: begin
clr_loops = 1'b1;
if (measure_valid) begin
next_state = 1'b1;
end
end
1: begin
inc_loops = 1'b1;
if (match | loops_max) begin
set_valid = 1'b1;
clr_loops = 1'b1;
next_state = 1'b0;
end
end
endcase
end
上面一段程序,从RTL视图可以看到,除current_state为寄存器之外,其余全部综合为了组合逻辑,所以说,reg定义的数据也不全会综合成寄存器。但是贴出程序还是为了要体会阻塞的用法。
好像记得有这样的说法:在组合逻辑中用阻塞,在时序逻辑中用非阻塞。
如果把上面的程序修改成非阻塞赋值,如下,这样会不会存在问题呢?我个人认为有可能,可能要看综合器本身,如果case语句外的next_state <= current_state与case内的next_state <= 1'b0(或者1‘b1)是随机的,那状态可能会出错。不会一般都不会(因为我以前就有这样做过,没问题(>'~'<))
reg current_state;
reg next_state;
reg inc_loops;
reg clr_loops;
reg set_valid;
reg [4-1:0] loops;
reg [23:0] threshold;
reg [28-1:0] dim_cnt_thresh_r;
reg [28-1:0] cnt_thresh;
always @ (posedge clk )
begin
current_state <= next_state;
end
always @ (*)
begin
next_state <= current_state;
clr_loops <= 1'b0;
inc_loops <= 1'b0;
set_valid <= 1'b0;
case (current_state)
0: begin
clr_loops <= 1'b1;
if (measure_valid) begin
next_state <= 1'b1;
end
end
1: begin
inc_loops <= 1'b1;
if (match | loops_max) begin
set_valid <= 1'b1;
clr_loops <= 1'b1;
next_state <= 1'b0;
end
end
endcase
end
不过在这篇博客中,也给出了一种表达
|
module and_nonblocking ( input a, input b, input c, output reg y ); always@* begin // y_entry = y y <= a; // y_exit = a y <= y & b; // y_exit = y_entry & b y <= y & c; // y_exit = y_entry & c end // y = y_exit endmodule
注意always块内的前2条语句将不会产生任何效果。上述always块等价与:
always@* y <= y & c;
FPGA Verilog语言中阻塞赋值与非阻塞赋值个人看法