笔者之前在阅读《Verilog HDL 高级数字设计》时的基4布斯乘法器一文时,就遇到了一段有问题的代码,而这个问题可以用Verilog基础:表达式位宽的确定(位宽拓展)文中的分析完美解决。
always @ (negedge clock)
if (Start) begin
expected_value = 0;
case({word1[word_size-1], word2[word_size-1]})
0: begin expected_value = word1 * word2; expected_mag = expected_value; end
1: begin expected_value = word1* {`All_Ones,word2[word_size-1:0]};
expected_mag = 1 + ~(expected_value); end
2: begin expected_value = {`All_Ones, word1[word_size-1:0]} *word2;
expected_mag = 1 + ~(expected_value); end
3: begin expected_value = ({`All_Zeros, 1 + ~word2[word_size-1:0]}) * ({`All_Zeros, 1 + ~word1[word_size-1:0]});
expected_mag = expected_value; end
endcase
上面的代码片作用是根据word1和word2的不同算出乘积结果的数值大小和结果的数值绝对值大小。即当两个数都是负数时,即case 3,对两个数都整体取反加一变成正数(对于一个补码,获取其相反数补码的操作是对整体取反加一,包括符号位),然后相乘获得结果。但是里面存在问题,就是word的片选在取反之前会先补零拓展至32位,因为不带位宽的1是32位的,根据位宽拓展原则+和~这两个操作符都是上下文决定操作符,因此会先将word2[word_size-1:0]补零拓展(任何信号的片选都是无符号数)至32位,此时再取反就会变成一个很大的正数,因此会出错。
对于比如8位有符号数-128和-127,按照上面转换后理想情况是128*127,但是因为错误的位宽拓展,结果变成了4294967168*4294967167。
解决这个问题的方法很简单,将1改成1'b1即可阻止不必要的位宽拓展,或者用{}拼接操作符包围~word2[word_size-1:0],因为所有在{}符号内的操作数都会变成自决定操作数,位宽由自己决定而不加入上下文环境中,如下所示的为正确的代码。
always @ (negedge clock)
if (Start) begin
expected_value = 0;
case({word1[word_size-1], word2[word_size-1]})
0: begin expected_value = word1 * word2; expected_mag = expected_value; end
1: begin expected_value = word1* {`All_Ones,word2[word_size-1:0]};
expected_mag = 1'b1 + {~(expected_value)}; end
2: begin expected_value = {`All_Ones, word1[word_size-1:0]} *word2;
expected_mag = 1'b1 + {~(expected_value)}; end
3: begin expected_value = ({`All_Zeros, 1'b1 + {~word2[word_size-1:0]}}) * ({`All_Zeros, 1'b1 + {~word1[word_size-1:0]}});
expected_mag = expected_value; end
endcase
上面的代码中,两种改正措施都实现了,实际上只需要其中一种即可。更多关于Verilog表达式位宽拓展和符号拓展相关的问题,可以查看下面的文章。
Verilog基础:表达式位宽的确定(位宽拓展)https://blog.csdn.net/weixin_45791458/article/details/128772558?spm=1001.2014.3001.5502Verilog基础:表达式符号的确定https://blog.csdn.net/weixin_45791458/article/details/128840843?spm=1001.2014.3001.5502