Verilog HDL 整数乘法器(转)

第一章 整数乘法器

1.1 整数的概念

整数在IEEE 的规定上有,短整数short integer , 中整数integer 和 长整数long integer ,它们之间的关系如下:

 

 

整数

字节空间

取值范围

短整数

一个字节

-127 ~ 127

中整数

两个字节

-32767~32767

长整数

和四个字节

-2147483647~2147483647

 

 

在这里笔者以短整数为笔记的主角。

 

 

短整数的最高位是符号位,符号位的正负表示了该值是“正还是负”?。正值的表示方法很简单,反之负值的表示方法是以补码来表示。

 

 

+127 亦即8'b0111_1111;

+4 亦即8'b0000_0100;

-127 亦即8'b1000_0001;

-4 亦即8'b1111_1100;

 

 

补码在英文又叫2nd implementation   , 其实是“正值的求反又加一”的操作。(哎~年轻时的笔者曾经这个东西头疼过)。一个负值表示如-4 ,是由+4 求反由加一后而成。

 

 

8'b0000_0100;  // 正值4

8'b1111_1011;  // 求反

8'b1111_1100;  // 加1 , 负值4

那么符号位和正值,负值,补码,取值由有什么关系呢?举个例子 :A = 8'b0111_1111 (+127) 和B = 8'b1000_0001 ( -127 )。

当我们在进行判断一个短整数是正值还是负值的时候,我们可以这样表示:

 if ( !A[7] ) ...  // A是正值

 if ( B[7] ) ...  // B是负值

在事实的事实上。我们知道短整数 28 ,亦即取值范围是0~255,但是符号位的出现吃掉了最高位,所以造成由28 的取值范围变成27 = 0~171 。

你知道吗?在短整数家族里面永远存在一个幽灵成员。该成员很神秘,它不是正值,即不是负值或者0值。而且它的能力也不可忽视,它划分了正值和负值的边界,它就是8'b1000_0000。

+127     8'b0111_1111;

划分      8'b1000_0000;

-127      8'b1000_0001;

换句话说,在8'b1000_0000 之前的都是正值 ,然而在8'b1000_0000 之后是负值。如果读者硬是要说8'b1000_0000 是 “负0”,笔记也无话可说......

从上述的内容,我们可以知道:正值可以进行求反又加一之后成为负值。那么负值如何变成正值?同样的一个道理“负值求反又加一后,成为正值”。

8'b1111_1100;  // 负4

8'b0000_0011;  // 求反

8'b0000_0100;  // 加1 , 正4

 

1.2 传统乘法的概念

笔者还记得笔者在上小学三年级的时候,老师在黑板上写上3 x 4 = 12。笔者对这神秘的数学公式迷糊了头脑。后来老师解释道: " 3粒苹果重复加上4 次等于12粒苹果",小时的笔者顿时恍然大悟!

当笔者上了初中,老师在黑板上写上3 + -4 = -1。大伙们都明白那是整数,但是初中的笔者,脑袋过很迟钝。因为在现实中,初中的笔者认为没有“-3粒苹果”类似实体的概念纯在,后来老师解释道:“ 小明欠小黄4粒苹果,后来小明还了小黄1粒苹果,结果小明还欠小黄一粒苹果 ”,初中的笔者又恍然大悟。

又在初中,当老师又在黑板上写上如下的内容。那时候的笔者,嘴巴长得大大 ,有好一段时间说不出话来 。好一段时间笔者都是自己在嘀咕....

 3 x 4 = 12;    " 3粒苹果重复叠加4次,等于12粒苹果"

-3 x 4 = -12;    " 欠3粒苹果,重复欠4次,等于欠12粒苹果"

3 x -4 = -12;    " 欠4粒苹果,重复欠3次,等于欠12粒苹果"

-3 x -4 = 12;    " @#¥%#¥*!%……" ( 嘀咕中... )

读者们不要笑,上述的故事确实是笔者的真实故事。那时候的笔者,真的拿不到整数的乘法的门儿,考试还常常满江红,真的悲剧的初衷时代......

在传统的概念上乘法等价于“重复几次”。打个比方:B = 4;A x B 亦即A要重复加四次才能得到答案。

然而在乘法中“负值正值的关系”就是“异或的关系”。

A值

B值

结果

正(0)

正(0)

正(0)

正(0)

负(1)

负(1)

负(1)

正(0)

负(1)

负(1)

负(1)

正(0)

 A x B = C;

 3 x 4 = 12;   

-3 x 4 = -12;   

3 x -4 = -12;   

-3 x -4 = 12; 

从上面的内容看来,无论A值和B值是什么样的“正值和负值的关系”,结果C都是一样。

那么我们可以换一个想法:

“在作乘法的时候只是我们只要对正值进行操作。然而“负值和正值的结果”,我们用“异或”关系来判断... ”

实验一 :传统的乘法器

该乘法器的大致操作如下:

(一)在初始化之际,取乘数和被乘数的正负关系,然后取被乘数和乘数的正值。

(二)每一次累加操作,递减一次乘数。直到乘数的值为零,表示操作结束。

(三)输出结果根据正负关系取得。

multiplier_module.v



第3~11行是该模块的输入输出。看到Start_Sig 和Done_Sig 是仿顺序操作的标志性结构,不明白的去看笔者之前写的笔记。Multiplicand 和Multiplier (被乘数和乘数),都是8位位宽,所以输出Product 是16位位宽。

第16~21行是该模块所使用的寄存器,i寄存表示步骤,Mcand 用来暂存Multiplicand 的正值,Mer 用来暂存Multiplier 的正值,Temp 寄存器是操作空间。然而isNeg 标志寄存器是用来寄存Multiplicand 和Multiplier 之间的正负关系。

在步骤0(36~45行)是初始化的步骤。第39行isNeg寄存“乘数和被乘数之间的正负关系”。第40行,Mcand寄存 Multiplicand 的正值,该行表示:如果被乘数的符号位是逻辑1的话,就将负值转换为正值,然后Mcand寄存该值,否则Mcand直接寄存Multiplicand 的值。第41行是用来寄存Multiplier 的正值,该行的操作和40行很相识。

在步骤1(47~49行),是“重复加几次”的操作。Temp寄存器的每一次值的叠加,Mer寄存就递减(49行)。直到Mer的值等于0(48行),就进入下一个步骤。步骤2~3是产生完成信号。

在62行,Product输出信号的输出值是由isNeg寄存器作决定,如果isNeg是逻辑1,那么Temp的结果从负值转换为正值。否则直接输出Temp的值。

multiplier_module.vt


 

第16~22行是复位信号和时钟信号的激励。第26~35行是multiplier_module.v 的实例化。

第39行以下和普通的仿顺序操作的写法一样,不明白的话请看笔者以往写过的笔记。

步骤0~3, 会输入不同的乘数和被乘数来激励multiplier_module.v。

仿真结果:


 实验说明:

其实传统的乘法器是很容易的,但是短整数的出现,负值和正值随着出现,使得设计上难以下手。但是只要掌握负值和正值的关系以后,乘法只作正值也“无问题”。只要在输出下一点手脚就行了。

实验结论:

传统的乘法器虽然简单,但是它有一个致命的问题。就是被乘数越大就越消耗时钟。具体的原因在下一章节解释......

1.3 传统乘法器的改进

Verilog HDL 语言所描述的乘法器的消耗是以“时钟”作为时间单位。反之,组合逻辑所建立的乘法器是以“广播时间”作为时间单位。说简单点就是,Verilog HDL 语言所描述的乘法器“快不快”是根据“时钟消耗”作为评估。

假设A = 10 , B = 20,  A x B ,那么时钟的消耗至少需要20个,因为A值需要累加20次才能得到结果。到底有没有什么办法,改进这个缺点呢?

有学过乘法的朋友都知道A ( B ) = B ( A )。如果以实验一的乘法器作为基础,那么A( B ) 和B( A ) 所消耗的时间就不一样了。所以我们可以这样改进:

如果被乘数小于乘数,那么被乘数和乘数互换。

{ Multiplier , Multiplicand } = Multiplicand < Multiplier ? { Multiplicand ,Multiplier } :

                         {Multiplier ,Multiplicand };

 

举个例子:Multiplicand = 2 ,Multiplicand = 10 ;

更换之前 被乘数2 需要10次的累加,才能得到结果。 更换之后 被乘数为10 乘数为2,亦即被乘数10只要累加2次就能得到结果。

如此一来,可以减少不少时钟的消耗。

 

实验二: 传统乘法器改进

和实验一相比,在进行累加操作之间,多了一个被乘数和乘数比较的步骤。

(一)在初始化之际,取乘数和被乘数的正负关系,然后取被乘数和乘数的正值。

(二)乘数和被乘数比较,如果被乘数小于乘数,结果乘数和被乘数互换。

(三)每一次累加操作,递减一次乘数。直到乘数的值为零,表示操作结束。

(四)输出结果根据正负关系取得。

multiplier_module_2.v


和实验一先比,添加了一个比较的步骤(46~49行)。仿真结果:

仿真.vt 文件和实验一样。


 

在仿真的结果上,10 x 2 和2 x 10 的时钟消耗都一样。

实验说明:

与实验一的乘法器比较,关于时钟的消耗多少都有改进。

实验结论:

传统的乘法器无论如何改进也好,当遇见如127 x 127 的乘数和被乘数,咋也看不出什么优化......

1.4 补码君存在的意义

每一个人都有存在的意义,有的人用一生的时间去寻找自己的存在意义,有的人则是经过生活的大反转,看到了自己存在意义,有的人则不闻不问... 当然补码也有存在的意义,只是在前面的实验被笔者滥用而已。

补码不仅可以执行正值和负值转换,其实补码存在的意义,就是避免计算机去做减法的操作。

     1101     -3补

+    1000    8

      0101    5

 

假设-3 + 8,只要将-3 转为补码形式,亦即0011 => 1101,然后和8,亦即1000相加

就会得到5,亦即0101。至于溢出的最高位可以无视掉。

1101     -3补

+     1110     -2补

   1011    -5补

其实你知道吗,如Quartus II 综合器 ,当我们使用“-”算术操作符的时候,其实就是使用补码的形式,具体如下:

A = 8'd5;

B = 8'd9;

A -B 等价于A + ( ~B + 1'b1 );

在实际的操作中,综合器都会如上优化。

 

1.5:Booth算法乘法器

传统的乘法器是有极限的,因此位操作乘法器就出现了。笔者在网上冲浪找资源的时候,还常常撞到许多稀奇古怪的位操作乘法器。但是有一种位操作乘法器,吸引了笔者的眼球,它就是Booth算法乘法器。实际上Booth 算法是一种“加码”乘法运算。

Booth 算法的概念也很简单,我们先从数学的角度去理解看看:

 

B[0]

B[-1]

加码结果

0

0

0(无操作)

0

1

1(+被乘数)

1

0

1(-被乘数)

1

1

0(无操作)

 

B[-1] 是什么?先假设B是2的,然而B的最低位的右边后一个“负一位”那就是B[-1]。

0010 0  // LSB 右边出现的就是-1位

那么上面那个加码表和乘法又有什么关系呢?其实要加码的目标是“乘数”,假设乘数为2, 那么乘数2的加码过程会是如下。

一开始的时候在乘数2的“负一位”加上一个默认0值

0010 0

先判断[0: -1],结果是2'b00,表示“0”亦即没有操作

0010 0

判断[2: 1],结果是2'b01,表示“1”亦即“-被乘数”操作

0010 0

判断[1: 0],结果是2'b10,表示“1”亦即“+被乘数”操作

0010 0

判断[3: 2],结果是2'b00,表示“0”亦即没有操作

0010 0

举个例子,被乘数为7,0111; 乘数为2,0010;结果会是什么?

      0111       - A被乘数

      0010 0  - B乘数

  ==========

      0110      - 乘数加码

  ==========

      0000     0

   111001      1 (- 7)

    0111       1 (+7)

   0000        0

  ==========

   0001110     14   

  ==========        

 

 


 

 

从左边的操作过程中,我们可以看到乘数被加码以后,

操作的结果是14。

从数学的角度看来&

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值