verilog中间结果位宽问题

verilog中的位宽一般都是确定的,大部分是根据表达式左边的变量位宽对表达式右边的数据进行截断(右边变量位数多)或者补零(右边变量位数少),但是verilog有一个隐藏的位宽问题,之前一直没有注意过,直到最近在读《verilog编程艺术》的时候注意到这个问题,刚好最近遇到了这个问题导致的错误。

这就是自决定表达式,不要小看这个自决定,这个词的意思就是表达式的位宽可能看编译器的心情,而有可能和设计人员的想法背离。首先来看这样一个问题。

`timescale 1ns/1ns
module test(
);

reg  [1023  :0]     out;

reg  [31    :0]     out_div_mult;
reg  [31    :0]     out_div_shif;
reg  [31    :0]     out_div1;
reg  [31    :0]     out_div2;
reg  [31    :0]     out_div3;
reg  [31    :0]     out_div4;
reg  [31    :0]     out_div5;
reg  [31    :0]     out_div6;

reg  [6     :0]     id; 

wire [11    :0]     id_32tiems1; 
wire [11    :0]     id_32tiems2; 

initial 
begin
        out = 1;
        id = 1;

        #20
        out_div1        <= out[( id           )<<5+:          32] ;
        out_div2        <= out[( id[4:0]      )<<5+:          32] ;
        out_div3        <= out[{2'b0,(id[4:0])}<<5+:          32] ;
        out_div4        <= out[( id+ 8'b0     )<<5+:          32] ;
        
        out_div5        <= out[((id[4:0])*32)+:               32] ;
        out_div6        <= out[((id[4:0])*5'd31+id[4:0])+:    32] ;

        out_div_mult    <= out[id_32tiems1+:                  32] ;
        out_div_shif    <= out[id_32tiems2+:                  32] ;
end

assign  id_32tiems1     = (id[4:0])<<5                          ;
assign  id_32tiems2     = (id[4:0])*32                          ;

/*iverilog */
initial
begin
    $dumpfile("wave.vcd"); //generate wave file named "wave.vcd""
    $dumpvars(0); //dump the wave of all signals

    #40 $finish;
end
/*iverilog */

endmodule

第一眼看起来,所有的8个结果应该是相同的,但仿真波形如下

可以看到有两个结果与其他结果不同,这是因为:

id的位数定义为7位,out_div_1用了7位,所以7位结果左移5位没有归零,而是成为7'b010_0000;但是out_div_2的代码里只用了低5位, 五位数左移五位会成为5'b0_0000;

这个问题解释了,那么另一个问题又出现了,为什么以下两行结果也不同呢?理论上左移5位和x32的结果是一样的。

        out_div2        <= out[( id[4:0]      )<<5+:          32] ;
        
        out_div5        <= out[((id[4:0])*32)+:               32] ;

这就涉及到自决定表达式位宽规则了

        从上图可以看到,移位运算的中间结果位宽取决于被移位的数,在上面两行代码的第一行中,中间结果位宽是5(最大值是31),移位结果为零。

        但是,乘法的位宽取决于乘数和被乘数两者位宽的最大值,刚好verilog对未指定位宽的数默认为32位,因此在上面两行代码的第一行中,中间结果位宽是32(最大值是2^32),乘法结果是32。

out_div2        <= out[( id[4:0]      )<<5+:          32] ;

out_div6        <= out[((id[4:0])*5'd31+id[4:0])+:    32] ;

这两个计算结果就是完全一样的, 因为乘法指定了位数5,所以中间结果都是5位的,中间算出来都是0,out_div的结果都是低32位,都是1。

所以可以看出来,不注意位宽规则,最后可能算出全然不同的结果,这一点大家可以查看《verilog编程艺术》进一步学习。

下附自动化仿真代码(保存改为.bat双击运行):

echo "start compile"
iverilog -g2012 -o wave.vvp test.v

echo "compile completed"
vvp wave.vvp

echo "open wave file with gtkwave"
gtkwave wave.vcd

pause
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值