在一开始学到阻塞和非阻塞的时候,所被告知的两者的区别就在于阻塞是串行的,非阻塞是并行的。但是虽然知道这个不同点,有些时候还是很难真正区分用两者电路的区别,在这就通过几个例子来解释一下。
以一个简单的串行流水线寄存器为例:d-q1-q2-q3.
1,采用阻塞赋值
代码:always @ (posedge clk) begin q1=d; q2=q1; q3=q2; end
这样的代码将会导致每一个寄存器输出都是d,即d将无延时的传送给q3(即q3=d)。因为采用的是阻塞赋值,当时钟上升沿到来时,先执行q1=d,再执行q2=q1。以此类推,最后所以寄存器的值都变成了d.反映到电路中的结果就是一个单独的直接从d到q3的寄存器,不符合流水线要求。
2,依然采用阻塞赋值,但是赋值顺序有变化
代码:always @ (posedge clk) begin q3=q2; q2=q1; q1=d; end
当我们改变赋值顺序后,综合的电路是符合要求的。当上升沿到来,先执行q3=q2,则q3的值是上一个时钟沿时q2的值。类似的,当上升沿到来后,每个寄存器的值都往后移动了一位,也就实现了预期的流水线结构。但是这样的缺点就在于我们要仔细的安排赋值的顺序,容易出错,且增加工作量。
3,采用非阻塞赋值
代码:always @ (posedge clk) begin q1<=d; q2<=q1; q3<=q2; end
由于非阻塞赋值是并发执行的,当时钟沿到来时,每个寄存器更新的值都是赋值寄存器在上一个时钟沿所被赋予的值,也就真正实现了流水线的结构。而且采用非阻塞赋值的好处在于,不用想阻塞赋值一样需要考虑赋值顺序问题。
另外一个问题是,为什么在组合逻辑中需要使用阻塞赋值?
在大多数情况下,都应该使用非阻塞赋值,这也比较符合电路并发执行的特点。但是在组合逻辑中并不一定。
例如:
always @ (a or b or c or d) begin
temp1<=a & b;
temp2<=c & d;
y<=temp1 | temp2;
end
在这个组合逻辑中,我们希望实现的是先将ab相与,cd相与,再将产生的temp1,temp2相或。但由于采用的是非阻塞赋值,是并发执行的。当执行y<=temp1 | temp2时,temp1,temp2的值并未更新,也就是说y输出的是上一个触发时temp1 | temp2的值,即延后了一次触发。
而如果此时采用阻塞赋值的话,则能正确实现电路。
不过对于这个问题,我还是觉得并不是说组合逻辑导致要使用阻塞赋值,而是说电路功能决定到底使用哪种。即便上面的例子是时序电路,如果要实现相同的逻辑,则依然要用阻塞赋值。而如果你是使用类似之前提到的移位的功能,则即便用的是组合逻辑,也需要用非阻塞赋值。
至于为什么会有组合电路用阻塞的说法,应该是大部分时候组合逻辑功能需要阻塞赋值,且这是一个规范吧,毕竟规范有时候不是很好解释,但是却在工作中大有裨益。当然,这只是我个人观点,有不同意见者希望不吝指教。