FPGA学习笔记---3种乘法器的比较

举报
莫言2021 发表于 2021/10/26 20:56:06 2021/10/26
【摘要】  没学FPGA的时候,以为计算乘法和加法一样简单,但是学习之后才发现,要设计一个好的乘法器并不简单。今天就先用一个简单的例子看看乘法是怎样实现的。先看第一种最简单直接的实现方式module mul(                                //两个8位 二进制乘法操作           out,           a,           b       ); pa...

 没学FPGA的时候,以为计算乘法和加法一样简单,但是学习之后才发现,要设计一个好的乘法器并不简单。今天就先用一个简单的例子看看乘法是怎样实现的。

先看第一种最简单直接的实现方式

module mul(                                //两个8位 二进制乘法操作
           out,
           a,
           b
       );
 
parameter size = 8;
 
input [size: 1] a,b;
output [2*size:1] out;
reg [2*size:1] out;
 
integer i;
 
//方法一:
always @(a or b) begin
    out = a * b;
end
 
endmodule
 
按照简单的方法,两个数直接相乘。

编译后占用资源情况


这种直接相乘的方法,没有占用逻辑资源,但是占用了一个M9K块。

仿真结果如下


第二种方法,通过移位和加法操作实现。

module mul(                                //两个8位 二进制乘法操作
           out,
           a,
           b
       );
 
parameter size = 8;
 
input [size: 1] a,b;
output [2*size:1] out;
reg [2*size:1] out;
 
integer i;
always @(a or b) begin
    out = 0;
    for(i=1;i<=size;i=i+1)
        if(b[i])
            out = out + (a << (i -1));
end
 
endmodule
 
编译后占用资源情况


这种仿真占用了134个逻辑资源。

仿真结果如下:


这种方法直接看代码看不出来具体的乘法操作是如下实现的,下面就一步步来分析一下实现过程。

假设a=2,b=3;

为了方便理解,将这两个乘数用二进制表示 a = 8'b0000_0010, b = 8'b0000_0011;

变量i的范围从1到8.

当i=1时,b[1] = 1,也就是b的最低位为1,out = 0,out = 0 + a<<(1-1), out = a; 计算结束后out = 2;

当i = 2时,b[2] = 1,也就是b的倒数第二位为1, out = 2, out = 2 + a<<(2-1) out = 2 + a<<1,a左移以为后值为 a = 8'b0000_0100; 

out = 2 + 4; out = 6;

当i = 3时,b[3] =0,条件不成立,不执行,由于b的其他位全部为0,所以直到 i=8时,条件都不成立。if语句不再执行。最后退出for循环,计算的结果就是out=6;

这个计算结果的核心就是,当b的某一位为1时,就将a左移。左移就相当于做乘法运算。也就是说将b的每一位都分解出来和a做乘法运算,然后再将所有的和累加起来。

a*b = 2*3 = 8'b10 * 8'b11 = 8'b10 * (8'b10 + 8'b01) = 8'b10 * 8'b10 + 8'b10 * 8'b01; a乘以8'b10就直接将a左移1位,a乘以8'b01就将a左移0位。

也就是将2*3分解为 2*(2+1) = 2* 2 + 2*1来进行计算。

假如a*b = 2*255 = 8'b10*8'b1111_1111,那么计算展开后就为

2*8'b1000_0000 + 2*8'b0100_0000 + 2*8'b0010_0000 + 2*8'b001_0000  + 2*8'b000_1000 + 2*8'b000_0100 + 2*8'b000_0010 + 2*8'b000_0001

将乘法转换成按位的左移运算,来减小资源的消耗。

下面再看第三种方法

module mul(                                //两个8位 二进制乘法操作
           out,
           a,
           b
       );
 
parameter size = 8;
 
input [size: 1] a,b;
output [2*size:1] out;
reg [2*size:1] out;
 
reg [2*size:1] tem_a;
reg [size:1] tem_b;
always @(a or b) begin
    out = 0;
     tem_a = a;
     tem_b = b;
     
   repeat(size) begin
        if(tem_b[1]) 
            out = out + tem_a;
        
        tem_a = tem_a << 1;        //左移一位
        tem_b = tem_b >> 1;        //右移一位
    end
    
end
 
endmodule
 
编译后占用资源情况


和方法2一样,消耗了134个逻辑资源。

仿真结果如下


第三种方法和第二种方法本质其实是一样的,第二种方法在for循环中控制 b 的下标来对数据a进行移位操作。第三种方法没有控制下标,而是每次将b右移一位,然后判断最低位,在决定是否对a进行移位运算,而a每次只左移1位。不过从逻辑上来讲,第三种方法理解起来更容易一点。

下面逐步分析一下第三种方法的计算过程

假设a=2,b=3;

为了方便理解,将这两个乘数用二进制表示 a = 8'b0000_0010, b = 8'b0000_0011;

第一次计算:先判断b的最低位是否为1,此时b的最低位为1。 计算 out = 0 + 8'b0000_0010;

然后a左移一位,移动后a的值为 a = a = 8'b0000_0100; b右移一位,移动后b的值为 b =  b = 8'b0000_0001;

进行第二次计算: b的最低位此时为1,计算 out = 8'b0000_0010 + 8'b0000_0100;

然后a左移一位,移动后a的值为 a = a = 8'b0000_1000; b右移一位,移动后b的值为 b =  b = 8'b0000_0000;

然后继续进行计算,由于b的值全部为0,所以不再执行out的计算过程,只执行a的左移运算和b的右移运算,直到退出repeat循环。

退出循环后out的值为  out = 8'b0000_0010 + 8'b0000_0100; out = 8'b0000_0110; 计算的结果out = 6;

通过上面的三个例子可以看出,要设计一个好的乘法器,核心就是如何将乘法操作通过各种方法转换成加法和移位的操作,以提高计算速度和占用更小的资源。
————————————————
版权声明:本文为CSDN博主「嵌入式@hxydj」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_20222919/article/details/108381634

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。