Verilog流水线加法器always块中应该采用阻塞赋值(=),还是非阻塞赋值(<=)?
图一
图二
图三
图四
最近写了一个16位二级流水线加法器,并进行了一下仿真。发现在always块中采用阻塞赋值(=)和非阻塞赋值(<=)的结果是不一样的,书上的例程以及网上很多例程的流水线加法器都采用的是阻塞赋值。
书上对流水线加法器的描述是这样的:“采用流水线,能将一个算术操作分解为一些小规模的基本操作,将进位和中间值存储在寄存器中,并在下一个时钟周期内继续运算”。
如上图所示,图一、图二是采用非阻塞赋值的代码及仿真波形图,图三、图四是采用阻塞赋值的代码及波形图。明显可以看出,采用阻塞赋值的仿真结果是在一个时钟周期内得到结果,并没有像描述的那样在每个时钟周期内分级运算,也没有体现出流水线的特点;而非阻塞赋值好像更符合“下一个时钟周期内继续运算”的描述,也体现出了流水线的执行特点。
求大神指点!
采用阻塞赋值在这里是很危险的行为,仿真和综合的结果可能会不同,两个always执行的顺序实际上是不固定的(虽然我们理解时认为是并行执行,但是因为时钟延时,时钟实际到达寄存器的时间是不同的),不过也可能会被综合成我们想要的电路,虽然比非阻塞赋值运算结果快了一个时钟周期,不过不建议使用阻塞赋值
你了解流水线加法器吗?书上用的是阻塞赋值,但是仿真结果却跟我想象的不一样。我感觉非阻塞赋值的仿真结果更符合流水线执行的特点。是否是我对流水线的理解有偏差?
我要是说这里用阻塞赋值是错误的,会不会有人喷死我
讲讲你的依据吧,我也感觉这里用阻塞赋值有问题,但是没有足够理论支撑
什么依据?流水线需要控制事件发生的前后顺序,上面的阻塞赋值并不能控制两个always事件发生的前后顺序,这算依据吗?你仿真只用了低8位,可以用满16位来仿真,再看一下综合后的RTL图,应该就可以看出来不同了
always@(posedege clk) 语句说明你要做的是同步逻辑设计,后面只能跟“非阻塞”赋值,而且你要做流水设计的话,就更应该是非阻塞赋值了;至于阻塞赋值,从语法上讲没有问题,但是最终综合成什么逻辑电路,就要完全看综合器对你这段代码的理解了,所以不同的软件版本之间,同一版本不同综合次数之间,不同的综合工具之间,最终综合结果都有可能不一样,属于不规范语法描述方式。所以不用去纠结第二代码了。
最后为什么不直接用
always@(posedge clk) begin
{cout,sum}<=cina+cinb;
end
不知道你把它分解成,高8Bit和低8bit分别相加是想干什么 ?
同意楼上的观点 LS+1
同意楼上的观点 LS+1
1、因为要采用流水线设计,所以没有直接用{cout,sum}。
2、采用图一代码,我发现仿真结果不对。上图中,我没有对高8位赋值。在我对高8位赋值后,发现:输出结果(cout)的高8位是当前输入高8位的和,而低8位则是上一个时钟输入低8位的和。请问应如何解决?不知您之前是否写过流水线加法器?求指教。
上面说错了一点,是输出结果(sum),而不是(cout),cout是进位位
您好小编。您再仔细分析一下您的代码就可以发现问题了。其实这和阻塞非阻塞没太大关系。(当然时序电路还是必须要用非阻塞的)
主要有
1.流水线的编写思路不太清晰。
2.always里面的加法和拼接符用的也有点问题。(写的是:{cout1,sum1}<={cina[7],cina[7:0]}+{cinb[7],cinb[7:0]}+cin;举个例子,如果低八位cina=a0;cinb=00;那么结果将是{1,a0},显示进位了。但实际并没有进位。所以有误。)
最主要的问题是流水线结构不对,根据您写的代码,我画了个RTL图,您看了图之后应该会很清楚了。
流水线的每一步都需要一个寄存器。
按图所示第一个时钟沿时,寄存器sum1寄存低八位的和值,但寄存器sum寄存的是上个时钟低八位部分的和值与当前输入值的和值。根本原因是因为高八位的加法器的输入随着cina和cinb改变而立即改变。所以才会出现您所说的高八位是当前的值,而低八位是上一个时钟的值。
正确的方法是要在画红圈处加一个寄存器锁存值。(可以锁存输入值,也可以锁存和值)
非阻塞,用assign语句的话,可以用阻塞!
不会,强烈支持!这和什么流水不流水没有关系!
您好!首先感谢您对我的问题作出如此详细的解答。
关于您所说的进位位以及每一级增加寄存器的问题,我后来也已经发现并改正了,但是使用阻塞赋值和非阻塞赋值的仿真结果仍然是不同的。阻塞赋值的仿真结果:输入和输出仍然是在一个时钟周期内完成,而非阻塞赋值的仿真结果:输入和输出却是阶梯状。
还望指点!谢谢!
指点不敢..我也在学习中,只能按我理解的观点谈一下问题点...
小编您可以试一下,源代码中,在用阻塞赋值时,如果将两个always调换位置(将第二个always写在前面,第一个always写在后面),仿真出来的结果会和原来的不一样。(sum要从第二个时钟沿才有输出,并且与当前的输入值之和不一致)
在用非阻塞赋值时,就算将两个always调换位置,仿真出来的结果是一样的。
根本原因在于,当仿真器运行到阻塞赋值语句时,先计算等号右边的值并更新,这时候赋值语句不允许任何别的verilog语句干扰,直到把该值赋值完成为止。
如果是非阻塞赋值语句,计算等号右边的值时,其他的verilog语句,非阻塞赋值语句都可以同时计算等号右边的值。非阻塞语句允许其他的语句同时操作。
按此理解,如果
1.原本代码,用阻塞:仿真器执行语句,按代码所述,第一个上升沿事件开始→计算出sum1和count1并立即赋值→计算cout和sum并立即赋值→结束上升沿事件。
2.原代码,用阻塞,将两个always对调:仿真器执行语句,第一个上升沿事件开始→计算出count和sum并立即赋值→计算出sum1和count1并立即赋值→结束上升沿事件。
3.原代码,用非阻塞:仿真器执行语句,第一个上升沿事件开始→计算出sum1和count1的值,同时计算sum,count的值(目标还未更新赋值)→更新赋值目标的值→结束上升沿事件。
情况1中,计算sum1与count1的阻塞赋值语句在前面,在sum与count执行之前,sum1与count1已经得到更新,所以第一个上升沿就有值输出。
情况2里面,由于把sum和count的语句写在了前面,仿真器先计算并更新它们俩的值,但由于此时sum1和count1仍是未知值,所以第一个时钟沿输出仍是未知值(红线),更新完sum与count后,才计算写在后面的always里面的sum1与count1的值并立即更新。
由于有这样的歧异,所以才可综合代码里,规定在含有时序逻辑的always块里面必须使用非阻塞。
由于流水线是典型的,严格的时序逻辑电路,所以必须使用非阻塞。
如果小编您还不太理解,建议多写一些测试的小代码,用两种赋值修改看看仿真有什么不同然后用自己的想法去理解。当理解的差不多了,再写一些测试代码预测一下仿真结果,或者多看书上的例子。
简单,你把输入数据cina和cinb延迟1级,简称cina_d1和cinb_d1,在你的第二个always块中,高8bit的运算用cina_d1和cinb_d1代替就OK了,改完相信你就知道你的问题在哪了。另外刚才看了12楼的解说,他说的完全正解!,低8位相加时,应该不需要进行符号位扩展,毕竟这里的第8bit不是符号位。这一点倒是看了12楼的观点才发现。
按照您所说的,我又试了多种情况,发现:代码写在一个always块和写在两个always块中的仿真结果是一样的,那么问题来了:如果触发条件相同,一个always块是否和多个always块的效果相同呢?多个always块的意义又何在呢?