FPGA学习笔记---3种乘法器的比较
没学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
- 点赞
- 收藏
- 关注作者
评论(0)