FPGA|CPLD|ASICwilliam hill官网
直播中

黄彭灿

9年用户 42经验值
擅长:可编程逻辑 连接器 电源/新能源 连接器 模拟技术 连接器 测量仪表 连接器 EMC/EMI设计 连接器 嵌入式技术 连接器 制造/封装 连接器 存储技术 连接器 连接器 接口/总线/驱动 连接器 处理器/DSP 连接器 光电显示 连接器 控制/MCU 连接器 EDA/IC设计 连接器 RF/无线 连接器 MEMS/传感技术 连接器
私信 关注
[问答]

Verilog流水线加法器always块中应该采用阻塞赋值(=),还是非阻塞赋值(<=)?

二级流水线加法器非阻塞赋值.png 图一
wave3.PNG
图二
二级流水线加法器阻塞赋值.png
图三
wave4.PNG
图四

最近写了一个16位二级流水线加法器,并进行了一下仿真。发现在always块中采用阻塞赋值(=)和非阻塞赋值(<=)的结果是不一样的,书上的例程以及网上很多例程的流水线加法器都采用的是阻塞赋值。
书上对流水线加法器的描述是这样的:“采用流水线,能将一个算术操作分解为一些小规模的基本操作,将进位和中间值存储在寄存器中,并在下一个时钟周期内继续运算”。
如上图所示,图一、图二是采用非阻塞赋值的代码及仿真波形图,图三、图四是采用阻塞赋值的代码及波形图。明显可以看出,采用阻塞赋值的仿真结果是在一个时钟周期内得到结果,并没有像描述的那样在每个时钟周期内分级运算,也没有体现出流水线的特点;而非阻塞赋值好像更符合“下一个时钟周期内继续运算”的描述,也体现出了流水线的执行特点。


求大神指点!




已退回5积分

回帖(17)

小白报道

2016-9-9 10:24:03
采用阻塞赋值在这里是很危险的行为,仿真和综合的结果可能会不同,两个always执行的顺序实际上是不固定的(虽然我们理解时认为是并行执行,但是因为时钟延时,时钟实际到达寄存器的时间是不同的),不过也可能会被综合成我们想要的威廉希尔官方网站 ,虽然比非阻塞赋值运算结果快了一个时钟周期,不过不建议使用阻塞赋值
举报

黄彭灿

2016-9-9 10:46:47
引用: 小白报道 发表于 2016-09-09 10:24
采用阻塞赋值在这里是很危险的行为,仿真和综合的结果可能会不同,两个always执行的顺序实际上是不固定的(虽然我们理解时认为是并行执行,但是因为时钟延时,时钟实际到达寄存器的时间是不同的),不过也可能会被综合成我们想要的威廉希尔官方网站 ,虽然比非阻塞赋值运算结果快了一个时钟周期,不过不建议使用阻塞赋值

你了解流水线加法器吗?书上用的是阻塞赋值,但是仿真结果却跟我想象的不一样。我感觉非阻塞赋值的仿真结果更符合流水线执行的特点。是否是我对流水线的理解有偏差?
举报

小白报道

2016-9-9 11:09:21
引用: 渊底一尾 发表于 2016-9-9 10:46
你了解流水线加法器吗?书上用的是阻塞赋值,但是仿真结果却跟我想象的不一样。我感觉非阻塞赋值的仿真结果更符合流水线执行的特点。是否是我对流水线的理解有偏差?

我要是说这里用阻塞赋值是错误的,会不会有人喷死我
举报

黄彭灿

2016-9-9 11:22:29
引用: 小白报道 发表于 2016-09-09 11:09
我要是说这里用阻塞赋值是错误的,会不会有人喷死我

讲讲你的依据吧,我也感觉这里用阻塞赋值有问题,但是没有足够理论支撑
举报

小白报道

2016-9-9 11:50:43
引用: 渊底一尾 发表于 2016-9-9 11:22
讲讲你的依据吧,我也感觉这里用阻塞赋值有问题,但是没有足够理论支撑

什么依据?流水线需要控制事件发生的前后顺序,上面的阻塞赋值并不能控制两个always事件发生的前后顺序,这算依据吗?你仿真只用了低8位,可以用满16位来仿真,再看一下综合后的RTL图,应该就可以看出来不同了
举报

alasga

2016-9-10 15:13:47
always@(posedege clk) 语句说明你要做的是同步逻辑设计,后面只能跟“非阻塞”赋值,而且你要做流水设计的话,就更应该是非阻塞赋值了;至于阻塞赋值,从语法上讲没有问题,但是最终综合成什么逻辑威廉希尔官方网站 ,就要完全看综合器对你这段代码的理解了,所以不同的软件版本之间,同一版本不同综合次数之间,不同的综合工具之间,最终综合结果都有可能不一样,属于不规范语法描述方式。所以不用去纠结第二代码了。
最后为什么不直接用
always@(posedge clk) begin
  {cout,sum}<=cina+cinb;
end
不知道你把它分解成,高8Bit和低8bit分别相加是想干什么 ?
举报

张晓亮

2016-9-10 15:35:55
同意楼上的观点 LS+1
举报

xiaozm008

2016-9-11 00:47:17
同意楼上的观点 LS+1
举报

黄彭灿

2016-9-12 08:34:44
引用: alasga 发表于 2016-09-10 15:13
always@(posedege clk) 语句说明你要做的是同步逻辑设计,后面只能跟“非阻塞”赋值,而且你要做流水设计的话,就更应该是非阻塞赋值了;至于阻塞赋值,从语法上讲没有问题,但是最终综合成什么逻辑威廉希尔官方网站 ,就要完全看综合器对你这段代码的理解了,所以不同的软件版本之间,同一版本不同综合次数之间,不同的综合工具之间,最终综合结果都有可能不一样,属于不规范语法描述方式。所以不用去纠结第二代码了。
最后为什么不直接用
always@(posedge clk) begin
  {cout,sum}

1、因为要采用流水线设计,所以没有直接用{cout,sum}。
2、采用图一代码,我发现仿真结果不对。上图中,我没有对高8位赋值。在我对高8位赋值后,发现:输出结果(cout)的高8位是当前输入高8位的和,而低8位则是上一个时钟输入低8位的和。请问应如何解决?不知您之前是否写过流水线加法器?求指教。
举报

黄彭灿

2016-9-12 08:56:44
引用: alasga 发表于 2016-09-10 15:13
always@(posedege clk) 语句说明你要做的是同步逻辑设计,后面只能跟“非阻塞”赋值,而且你要做流水设计的话,就更应该是非阻塞赋值了;至于阻塞赋值,从语法上讲没有问题,但是最终综合成什么逻辑威廉希尔官方网站 ,就要完全看综合器对你这段代码的理解了,所以不同的软件版本之间,同一版本不同综合次数之间,不同的综合工具之间,最终综合结果都有可能不一样,属于不规范语法描述方式。所以不用去纠结第二代码了。
最后为什么不直接用
always@(posedge clk) begin
  {cout,sum}

上面说错了一点,是输出结果(sum),而不是(cout),cout是进位位
举报

hqbenson

2016-9-12 16:23:15
本帖最后由 hqbenson 于 2016-9-12 16:34 编辑

您好楼主。您再仔细分析一下您的代码就可以发现问题了。其实这和阻塞非阻塞没太大关系。(当然时序威廉希尔官方网站 还是必须要用非阻塞的)
主要有
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改变而立即改变。所以才会出现您所说的高八位是当前的值,而低八位是上一个时钟的值。
正确的方法是要在画红圈处加一个寄存器锁存值。(可以锁存输入值,也可以锁存和值)
キャプチャ.PNG
举报

94沧海一粟0

2016-9-12 16:35:07
非阻塞,用assign语句的话,可以用阻塞!
举报

冷锋

2016-9-14 10:15:11
引用: 小白报道 发表于 2016-9-9 11:09
我要是说这里用阻塞赋值是错误的,会不会有人喷死我

不会,强烈支持!!!!!这和什么流水不流水没有关系!!!
举报

黄彭灿

2016-9-14 10:19:04
引用: hqbenson 发表于 2016-9-12 16:23
您好楼主。您再仔细分析一下您的代码就可以发现问题了。其实这和阻塞非阻塞没太大关系。(当然时序威廉希尔官方网站 还是必须要用非阻塞的)
主要有
1.流水线的编写思路不太清晰。

您好!首先感谢您对我的问题作出如此详细的解答。
关于您所说的进位位以及每一级增加寄存器的问题,我后来也已经发现并改正了,但是使用阻塞赋值和非阻塞赋值的仿真结果仍然是不同的。阻塞赋值的仿真结果:输入和输出仍然是在一个时钟周期内完成,而非阻塞赋值的仿真结果:输入和输出却是阶梯状。
还望指点!谢谢!
举报

hqbenson

2016-9-14 12:36:52
引用: 渊底一尾 发表于 2016-9-14 10:19
您好!首先感谢您对我的问题作出如此详细的解答。
关于您所说的进位位以及每一级增加寄存器的问题,我后来也已经发现并改正了,但是使用阻塞赋值和非阻塞赋值的仿真结果仍然是不同的。阻塞赋值的仿真结果:输入和输出仍然是在一个时钟周期内完成,而非阻塞赋值的仿真结果:输入和输出却是阶梯状。
还望指点!谢谢! ...

指点不敢..我也在学习中,只能按我理解的观点谈一下问题点...
楼主您可以试一下,源代码中,在用阻塞赋值时,如果将两个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块里面必须使用非阻塞。
由于流水线是典型的,严格的时序逻辑威廉希尔官方网站 ,所以必须使用非阻塞。

如果楼主您还不太理解,建议多写一些测试的小代码,用两种赋值修改看看仿真有什么不同然后用自己的想法去理解。当理解的差不多了,再写一些测试代码预测一下仿真结果,或者多看书上的例子。


举报

alasga

2016-9-14 22:24:13
本帖最后由 alasga 于 2016-9-14 22:38 编辑
引用: 渊底一尾 发表于 2016-9-12 08:34
1、因为要采用流水线设计,所以没有直接用{cout,sum}。
2、采用图一代码,我发现仿真结果不对。上图中,我没有对高8位赋值。在我对高8位赋值后,发现:输出结果(cout)的高8位是当前输入高8位的和,而低8位则是上一个时钟输入低8位的和。请问应如何解决?不知您之前是否写过流水线加法器?求指教。 ...

简单,你把输入数据cina和cinb延迟1级,简称cina_d1和cinb_d1,在你的第二个always块中,高8bit的运算用cina_d1和cinb_d1代替就OK了,改完相信你就知道你的问题在哪了。另外刚才看了12楼的解说,他说的完全正解!!!,低8位相加时,应该不需要进行符号位扩展,毕竟这里的第8bit不是符号位。这一点倒是看了12楼的观点才发现。
举报

黄彭灿

2016-9-15 23:40:03
引用: hqbenson 发表于 2016-09-14 12:36
指点不敢..我也在学习中,只能按我理解的观点谈一下问题点...
楼主您可以试一下,源代码中,在用阻塞赋值时,如果将两个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块里面必须使用非阻塞。
由于流水线是典型的,严格的时序逻辑威廉希尔官方网站 ,所以必须使用非阻塞。

如果楼主您还不太理解,建议多写一些测试的小代码,用两种赋值修改看看仿真有什么不同然后用自己的想法去理解。当理解的差不多了,再写一些测试代码预测一下仿真结果,或者多看书上的例子。

按照您所说的,我又试了多种情况,发现:代码写在一个always块和写在两个always块中的仿真结果是一样的,那么问题来了:如果触发条件相同,一个always块是否和多个always块的效果相同呢?多个always块的意义又何在呢?
举报

更多回帖

发帖
×
20
完善资料,
赚取积分