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

tech2023-02-08  112

     没学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;

通过上面的三个例子可以看出,要设计一个好的乘法器,核心就是如何将乘法操作通过各种方法转换成加法和移位的操作,以提高计算速度和占用更小的资源。

 

最新回复(0)