Verilog中阻塞与非阻塞的区别

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

从上面的例子中,我们可以看出,在阻塞赋值语句中,赋值的次序非常重要,而在非阻塞赋值语句中,赋值的次序并不重要。

下面我们具体分析一下阻塞和非阻塞赋值的语义本质:

阻塞:在本语句中“右式计算”和“左式更新”完全完成之后,才开始执行下一条语句;

非阻塞:当前语句的执行不会阻塞下一语句的执行。

先看阻塞赋值的情况:

我们来看这段代码:

always @(posedge Clk)

begin

Q1 = D;

Q2 = Q1;

Q3 = Q2;

end

always语句块对Clk的上升沿敏感,当发生Clk 0~1的跳变时,执行该always语句。

在begin...end语句块中所有语句是顺序执行的,而且最关键的是,阻塞赋值是在本语句中“右式计算”和“左式更新”完全完成之后,才开始执行下一条语句的。

在本例中,D的值赋给Q1以后,再执行Q2 = Q1;同样在Q2的值更新以后,才执行Q 3 = Q2。这样,最终的计算结果就是Q3 = D。

所有的语句执行完以后,该always语句等待Clk的上升沿,从而再一次触发begin...end 语句。

接下来,再看看非阻塞赋值的情况。

所谓非阻塞赋值,顾名思义,就是指当前语句的执行不会阻塞下一语句的执行。

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。

注:

*仿真器首先按照仿真时间对事件进行排序,然后再在当前仿真时间里按照事件的优先级顺序进行排序。

*活跃事件是优先级最高的事件。在活跃事件之间,它们的执行顺序是随机的。阻塞赋值(=)、连续赋值(assign)以及非阻塞赋值的右式计算等都属于活跃事件。

下面通过一个典型案例,进一步说明阻塞赋值和非阻塞赋值的区别。

这里有一个数组:Data[0]、Data[1]、Data[2]和Data[3],它们都是4比特的数据。我们需要在它们当中找到一个最小的数据,同时将该数据的索引输出到LidMin中,这个算法有点类似于“冒泡排序”的过程,而且需要在一个时钟周期内完成。例如,如果这4个数据中Data[2]最小,那么LidMin的值则为2。

module Bubble_Up(

Rst_n,

Clk,

Data,

Lid_Min

);

input Rst_n;

input Clk;

input [5:0] Data [0:3];

output [1:0] Lid_Min;

reg [1:0] Lid_Min;

always @(posedge Clk or negedge Rst_n)

begin

if (~Rst_n)

begin

Lid_Min <= 2'd0;

end

else

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

end

endmodule

我们的原意是首先将Lid_Min设置为一个初始值(任意值都可以),然后将Data[0]~Dat a[3]与Data[Lid_Min]进行比较,每比较一个数,就将较小的索引暂存在Lid_Min中,然后再进行下一次比较。当4组数据比较完成之后,最小的数据索引就会保留在Lid_Min中。

我们在以上代码中使用了非阻塞赋值,结果发现,仿真波形根本不是我们所需要的功能,如图所示,图中的Data[0]~Data[3]分别为11、3、10和12,Lid_Min的初始值为0。按道理来说,Lid_Min的计算结果应该为1,因为Data[1]最小,但仿真波形却为2。

为什么会得出这样的结果呢?

在时钟上升沿到来以后,且Rst_n信号无效时开始执行以下4个语句,假设这时候的Lid _Min是0,Data[0]~Data[3]分别为11、3、10和12:

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;

相关文档
最新文档