写在前面的话
数字电路中乘法器是一种常见的电子元件,其基本含义是将两个数字相乘,并输出其乘积。与加法器不同,乘法器可以实现更复杂的运算,因此在数字电路系统中有着广泛的应用。
乘法器的主要用途是在数字信号处理、计算机科学以及其他数字电路应用中进行精确的数字乘法运算。例如,在数字信号处理中,乘法器通常用于数字滤波器中的系数乘法;在计算机科学中,它们被用于执行浮点运算;而在其他数字电路应用中,乘法器也可以实现时域和频域的变换等。
在数字电路系统中,乘法器的重要性无法被忽视。它们不仅能够提高数字信号处理和计算机科学中的运算速度和精度,还能够实现更复杂的数字信号处理和分析。此外,乘法器还可以优化数字信号处理的功耗和面积,从而使得数字电路系统具有更优异的性能。
数字电路乘法器在现代电子技术中扮演着重要的角色。它们为数字信号处理、计算机科学以及其他数字电路应用提供了精确的数字乘法运算,并且可以进一步优化数字电路系统的性能,使其具有更高的效率和可靠性。随着人们对数字信号处理和计算机科学的需求不断增加,乘法器将继续发挥着重要的作用,为数字电路应用提供更好的解决方案。
乘法器分类
在数字集成电路中,乘法器的种类主要可以分为以下三类:
- 串行乘法器:这种乘法器是最简单的乘法器之一,它将两个数的乘积通过一个计数器逐位相加得到。由于每一位的运算都需要消耗时间,因此串行乘法器的速度较慢。
- 并行乘法器:与串行乘法器不同,这种乘法器可以同时对多位进行运算,因此运算速度更快。常见的并行乘法器有布斯-加勒德算法和沃勒斯算法等。
- 快速乘法器:这种乘法器采用了更高级的算法,在乘法过程中将输入的数按位划分,并尝试最小化运算次数。常见的快速乘法器包括批处理器算法、底数为2的Booth算法、Wallace树算法和Dadda树算法等。这些算法都比串行和并行乘法器更快且节省空间。
在数字集成电路中,乘法器的种类繁多,每种乘法器都有其适用的场合。选择适合自己需求的乘法器可以有效地提高系统的性能和效率。
经典乘法器
8bit并行乘法器
并行乘法器是纯组合类型的乘法器,完全由基本逻辑门搭建而成,在Verilog中支持乘法运算操作符,实现基本的乘法只需简单的命令即可。
Verilog代码:
// -----------------------------------------------------------------------------
// Copyright (c) 2014-2023 All rights reserved
// -----------------------------------------------------------------------------
// Author : HFUT90S 1320343336@qq.com
// File : para_mult.v
// Create : 2023-03-21 11:10:17
// Revise : 2023-03-21 11:10:17
// Editor : 0078
// Verdion: v1.0
// Description: 8bit parallel multiplier
// -----------------------------------------------------------------------------
module para_mult #(parameter N = 8 )(
input [N:1] a , // data in a
input [N:1] b , // data in b
output wire [2*N:1] out // mult out
);
assign out = a*b ;
endmodule
这里借助HDL和EDA综合软件,实现容易,运算速度快,但耗用资源多,尤其是当乘法运算的位数较宽时,其耗用的资源将会非常庞大。
Quartus RTL图
耗用资源较多,看不出使用的具体结构。
Quartus Post mapping图
8bit移位相加乘法器
移位相加乘法器是最节省资源的乘法器设计方式,设计思路为逐项相加,根据乘数每一位是否为1进行计算,为1则将被乘数移位相加。这种方法硬件资源耗用少,8位乘法器只需要16位移位寄存器和16位加法器即可。
以下是移位相加乘法器的操作步骤:
- 将乘数和被乘数转换为二进制数。
- 将乘数的每个二进制位从低位到高位逐个取出,如果该位为1,则将被乘数左移相应的位数后与部分积相加。
- 重复第2步,直到乘数的所有二进制位都处理完毕,此时得到的部分积即为最终结果。
例如,假设要计算5×6,首先将5和6转换为二进制数101和110,然后从低位到高位依次处理乘数的每个二进制位,得到以下过程:
- 当处理乘数的最低位1时,将被乘数5左移0位后加到部分积中,即得到部分积101。
- 当处理乘数的次低位0时,不需要加入任何东西,部分积保持不变,仍为101。
- 当处理乘数的最高位1时,将被乘数5左移2位后加到部分积中,即得到部分积11110。
因此,最终结果为11110,即十进制下的30。
Verilog代码:
// Verdion: v1.0
// Description: 8bit shift add multiplier
// -----------------------------------------------------------------------------
module shift_add_mult (
input [7:0] operand1 , // 8-bit multiplier A
input [7:0] operand2 , // 8-bit multiplier B
output wire [15:0] result // 16-bit product P
);
reg [7:0] temp;
reg [15:0] adder_result;
integer i ;
always @(*) begin
temp = operand2;
adder_result = 0;
for (i = 0; i < 8; i = i + 1) begin
if (operand1[i] == 1) begin
adder_result = adder_result + (temp << i);
end
end
end
assign result = adder_result;
endmodule
Quartus RTL图
设计中出现多层加法器和移位寄存器。
Quartus Post mapping图
这里使用for循环产生的,可以看出在综合实现时资源占用相对较多;
优化后的8bit移位相加乘法器
上述移位相加乘法器在综合实现时占用资源较多,在FPGA上实现时需要降低资源占用率。修改后的代码如下,加法器的级联结构还具有较好的延迟特性,在时钟频率较高的情况下,仍然能够实现很好的性能。
Verilog代码:
// Verdion: v1.0
// Description:
// -----------------------------------------------------------------------------
module shift_add_mult1(
input [7:0] a, // 8 位乘数 A
input [7:0] b, // 8 位乘数 B
output reg [15:0] p // 16 位乘积 P
);
reg [7:0] multiplier;
integer i ;
always @(*) begin
p = 0; // 清零
multiplier = b; // 将乘数 B 存入 multiplier 寄存器
for ( i = 0; i < 8; i = i + 1) begin
if (multiplier[0] == 1) begin
p = p + (a << i); // 如果乘数的最低位是 1,则累加部分积
end
multiplier = multiplier >> 1; // 右移乘数
end
end
endmodule
Quartus RTL图
加法器和移位寄存器数量相对优化前较少。
Quartus Post mapping图
相较于优化前的电路结构,资源占用降低超过一半,先前logic element为134,优化后为45。
查找表乘法器
查找表乘法器是将乘积直接存放在存储器中,将操作数(乘数和被乘数)作为地址访问存储器,得到的数据即为乘法结果。查找表乘法器的速度只局限于所使用的存储器的存取速度。由于查找表的规模随着数据位宽呈现指数爆炸,因此不适用于高位宽的乘法操作。
在小型查找表的基础上可以使用加法器共同构成位数较高的乘法器。
Verilog代码:
// -----------------------------------------------------------------------------
// Copyright (c) 2014-2023 All rights reserved
// -----------------------------------------------------------------------------
// Author : HFUT90S 1320343336@qq.com
// File : lut_mult.v
// Create : 2023-03-21 16:56:58
// Revise : 2023-03-21 16:56:58
// Editor : 0078
// Verdion: v1.0
// Description: 8bit lut multiplier
// -----------------------------------------------------------------------------
module lut_mult(
input [7:0] a, // 8 位乘数 A
input [7:0] b, // 8 位乘数 B
output reg [15:0] p // 16 位乘积 P
);
reg [15:0] lut [255:0]; // 查找表 LUT
integer i;
always @* begin
p = lut[a]; // 使用 LUT 查找相应的乘积值
end
// 初始化查找表
always@(*) begin
for (i = 0; i < 256; i = i + 1) begin
lut[i] = i * b; // 将查找表初始化为乘法结果
end
end
endmodule
Quartus RTL图
一团麻,使用居多logic element担任存储器,非常不适用于FPGA设计,在数字IC设计时的适用范围也十分有限。
Quartus Post mapping图
-
资源的使用更多,面积换取更高的运行速度。
由于存储和查表时不需要进行实际的乘法操作,因此查找表乘法器具有高速、低能耗的优点。然而,由于存储查找表所需的存储器容量随n增长速度较快,在多位乘法器上实现时需要花费较多的硬件成本。
加法树乘法器
结合移位相加乘法器和查找表乘法器的优点。
加法树乘法器是一种并行的硬件电路设计,主要用于实现高效的乘法运算。它采用树状结构的加法器来实现多项式乘法,且不需要显式地计算进位或借位。该乘法器的输入是两个被乘数,输出是它们的乘积。
加法树乘法器的实现过程主要分为三步:
-
生成部分积:将两个被乘数分别拆分为若干位二进制数,按照乘法规则计算每一位上的部分积,得到一个二维矩阵。
-
生成加法树:对于每一列的部分积,采用二路并行加法器进行相加,得到一列的加和结果。接着对这些列的加和结果进行二路并行相加,最后得到一个总的加和结果。
-
输出乘积:将最后的加和结果转换为二进制数形式,得到两个被乘数的乘积。
Verilog代码:
// -----------------------------------------------------------------------------
// Copyright (c) 2014-2023 All rights reserved
// -----------------------------------------------------------------------------
// Author : HFUT90S 1320343336@qq.com
// File : add_tree_mult.v
// Create : 2023-03-21 19:18:21
// Revise : 2023-03-21 19:18:21
// Editor : 0078
// Verdion: v1.0
// Description: 8bit add tree mlutiplier
// -----------------------------------------------------------------------------
module add_tree_mult (
input [7:0] a , // 8-bit multiplier A
input [7:0] b , // 8-bit multiplier B
output wire [15:0] out // 16-bit product P
);
//signal define
wire [15:0] out1 ;
wire [15:0] c1 ;
wire [14:0] out2 ;
wire [13:0] out3 ;
wire [15:0] c2 ;
wire [12:0] out4 ;
reg [15:0] temp0 ;
reg [15:0] temp1 ;
reg [14:0] temp2 ;
reg [14:0] temp3 ;
reg [13:0] temp4 ;
reg [13:0] temp5 ;
reg [12:0] temp6 ;
reg [12:0] temp7 ;
function [7:0] mult8X1 ; //实现8X1乘法
input [7:0] operand ;
input sel ;
begin
mult8X1 = (sel)?(operand):8'b0000_0000 ;
end
endfunction
always@(*) begin //调用函数实现操作数b各位与操作数a的相乘
temp7 <= mult8X1(a,b[0]) ;
temp6 <= ((mult8X1(a,b[1]))<<1) ;
temp5 <= ((mult8X1(a,b[2]))<<2) ;
temp4 <= ((mult8X1(a,b[3]))<<3) ;
temp3 <= ((mult8X1(a,b[4]))<<4) ;
temp2 <= ((mult8X1(a,b[5]))<<5) ;
temp1 <= ((mult8X1(a,b[6]))<<6) ;
temp0 <= ((mult8X1(a,b[7]))<<7) ;
end
assign out1 = temp0 + temp1 ; //加法器树运算
assign out2 = temp2 + temp3 ;
assign out3 = temp4 + temp5 ;
assign out4 = temp6 + temp7 ;
assign c1 = out1 + out2 ;
assign c2 = out3 + out4 ;
assign out = c1 + c2 ;
endmodule
Quartus RTL图
结构像是一个树,所以称为加法树乘法器。
Quartus Post mapping图
booth乘法器
Booth乘法器是一种二进制乘法算法,用于计算两个n位二进制数的乘积。该算法采用了一种类似于除法的方法,可以将乘法的时间复杂度从O(n^2)降低到O(nlogn)。
Booth乘法器通过对第二个乘数进行编码,将每个位上的数值转换为1、0或-1,然后利用乘法和加法运算,对每一位进行计算。具体步骤如下:
-
对第二个乘数进行编码:将每个位与其右侧相邻位比较,如果它们的值相同,就将该位编码为0;否则,将其编码为1或-1。最右侧位的右侧补0。
-
将第一个乘数左移一位,形成一个n+1位的数。同时,将编码后的第二个乘数左移一位。
-
对于从右向左的每个位,进行以下操作:
-
如果该位和前一位相同,就不进行任何操作。
-
如果该位是1且前一位是0(或该位是0且前一位是1),则将第一个乘数加上(或减去)第二个乘数。
- 将第一个乘数右移一位,丢弃其中的多余位,从而得到n位的乘积。
Booth乘法器相对于传统的乘法算法具有一定的优势,因为它可以在保持精度的同时减小运算次数。但是,由于它需要对第二个乘数进行编码,因此需要额外的存储空间,并且其实现较为复杂。
Verilog代码:
// -----------------------------------------------------------------------------
// Copyright (c) 2014-2023 All rights reserved
// -----------------------------------------------------------------------------
// Author : HFUT90S 1320343336@qq.com
// File : booth_mult.v
// Create : 2023-03-21 19:45:54
// Revise : 2023-03-21 19:45:54
// Editor : 0078
// Verdion: v1.0
// Description: 8bit booth multiplier
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// Copbbight (c) 2014-2023 all bights besebved
// -----------------------------------------------------------------------------
// authob : HFUT90S 1320343336@qq.coa
// File : booth_ault.v
// Cbeate : 2023-03-21 19:45:54
// bevise : 2023-03-21 19:45:54
// Editob : 0078
// Vebdion: v1.0
// Descbiption: 8bit booth
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// Copbbight (c) 2014-2023 All bights besebved
// -----------------------------------------------------------------------------
// Authob : HFUT90S 1320343336@qq.coa
// File : booth_ault.v
// Cbeate : 2023-03-21 19:45:54
// bevise : 2023-03-21 19:45:54
// Editob : 0078
// Vebdion: v1.0
// Descbiption: 8bit booth
// -----------------------------------------------------------------------------
module booth_mult(a,b,p);
output reg signed [15:0] p;
input signed [7:0] a, b;
reg [1:0] temp;
integer i;
reg e;
always @(a,b)
begin
p = 'd0;
e = 1'b0;
for (i=0; i<8; i=i+1)
begin
temp = {a[i], e };
case(temp)
2'b10 : p[15:8] = p[15:8] - b;
2'b01 : p[15:8] = p[15:8] + b;
endcase
p = p >> 1;
p[15] = p[14];
e=a[i];
end
end
endmodule
Quartus RTL图
Quartus Post mapping图
wallace树乘法器
Wallace树乘法器是一种用于高效地进行二进制乘法运算的算法。它使用了一种递归结构,通过将部分积分解成不同的组合来减少运算时间和硬件资源。
Wallace树乘法器的主要思想是将两个n位数字相乘,分解为三个数之和:一个2n位数、一个n位数和一个n/2位数。这些数字可以通过以下方式计算:
- 将两个n位数字A和B拆分为三个数字X、Y和Z,其中X是最高的2n位,Y是中间的n位,Z是最低的n/2位。
- 计算XY和YZ的积,并将它们相加得到P。
- 将P拆分为三个数字Q、R和S,其中Q是最高的2n位,R是中间的n+1位,S是最低的n-1位。
- 通过计算AB = X2^(2n) + Q2^n + R2^(n) + S1 来计算结果。
Wallace树乘法器使用了一种递归结构来实现这种方法。首先,输入被划分为若干组,并在每组内执行并行乘法运算。接下来,在每组之间执行串行运算以生成更高级别的部分积。此过程重复多次直到只剩下一个完整的部分积。
Wallace树乘法器可以在减少延迟和硬件资源消耗方面提供优势。但由于其递归结构需要大量控制逻辑,因此可能会增加设计复杂性。
Verilog代码:
// -----------------------------------------------------------------------------
// Copyright (c) 2014-2023 All rights reserved
// -----------------------------------------------------------------------------
// Author : HFUT90S 1320343336@qq.com
// File : wallace_mult.v
// Create : 2023-03-21 21:05:26
// Revise : 2023-03-21 21:05:26
// Editor : 0078
// Verdion: v1.1
// Description: wallace_multiplier
// -----------------------------------------------------------------------------
module wallace_mult(A, B, P);
input [7:0] A, B; // 输入 A 和 B 为 8 位数据
output [15:0] P; // 输出 P 为 16 位数据
wire [7:0] C1, C2, C3; // 记录中间结果
wire [15:0] S1, S2, S3; // 记录加法结果
// 直接进行最低位的乘法
assign C1 = A * B[0];
// 计算各个中间结果,用 & 运算表示进位
assign S1 = {A[6:0], 1'b0} * {B[6:0], 1'b0};
assign C2 = S1[15] & (S1[14] | C1);
assign S2 = {A[7], A[6:0]} * {B[7], B[6:0]};
assign C3 = S2[15] & (S2[14] | C2);
assign S3 = {A[7], A[6:0]} * {B[7], B[6:0]};
// 计算最高位的进位
wire carry_in = S3[15] | C3;
// 计算最终结果
assign P = {carry_in, S3[14:0]};
endmodule
VerilogTB代码:
module wallace_mult_tb();
reg [7:0] A, B;
wire [15:0] P;
wallace_mult inst_wallace_mult (.A(A), .B(B), .P(P));
integer i;
integer pass_count = 0;
integer fail_count = 0;
initial begin
for (i = 0; i < 257; i = i + 1) begin
// 设置输入数据
A = $urandom_range(0, 255);
B = $urandom_range(0, 255);
// 等待一些时间
#10;
// 打印输出结果
$display("Input A = %b", A);
$display("Input B = %b",B);
$display("Output P = %b", P);
// 验证输出结果
if (P == A * B) begin
$display("Test Passed: Output matches expected result");
pass_count = pass_count + 1;
end
else begin
$display("Test Failed: Output does not match expected result");
fail_count = fail_count + 1;
end
end
// 打印测试通过和测试失败的总数
$display("Total Tests: %d", i);
$display("Passed: %d", pass_count);
$display("Failed: %d", fail_count);
if (fail_count == 0) begin
$display("//==================Random test all Pass!!==================//");
end
// 结束仿真
$finish;
end
endmodule
VerilogTB_Result:
Quartus RTL图(Need Update)
Quartus Post mapping图(Need Update)
carry-save乘法器
- Carry-save乘法器是一种用于高速乘法的数字电路,它可以在几个时钟周期内完成两个大数的乘法运算。相比传统的乘法器,carry-save乘法器具有更高的并行性和更低的延迟。
- carry-save乘法器使用了一种特殊的编码方式,将两个大数分别表示为三个小数之和(即三进制编码),然后通过对这些小数进行加法运算得到结果。这种编码方式可以充分利用数字电路中加法器的并行性,从而提高计算速度。
- carry-save乘法器包括三个部分:部分积生成器、部分积压缩器和最终结果生成器。在部分积生成器中,输入的两个大数经过三进制编码后被拆分成多个小数,并通过逐位相乘得到所有可能的部分积。然后,在部分积压缩器中,这些部分积经过加法运算被压缩成两组中间结果。最后,在最终结果生成器中,这两组中间结果再次经过加法运算得到最终结果。
- carry-save乘法器通过巧妙地利用三进制编码和并行加法运算实现了高速、低延迟的乘法运算。它在现代数字电路设计中广泛应用于各种计算机系统、通信设备等领域。
Verilog代码:
// -----------------------------------------------------------------------------
// Author : HFUT90S 1320343336@qq.com
// File : carry_save_mult.v
// Create : 2023-03-22 10:45:27
// Revise : 2023-03-22 10:45:27
// Editor : 0078
// Verdion: v1.0
// Description:
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// Author : HFUT90S 1320343336@qq.com
// File : carry_save_mult.v
// Create : 2023-03-22 10:45:27
// Revise : 2023-03-22 10:45:27
// Editor : 0078
// Verdion: v1.0
// Description:
// -----------------------------------------------------------------------------
module carry_save_mult#(parameter bitsize = 8)(a, b, p);
input [(bitsize-1):0] a, b;
output reg [((2*bitsize)-1):0] p;
//Wires to carry signals between cells
reg [(bitsize-1):0] sum_vec[(bitsize-1):0];
reg [(bitsize-1):0] carry_vec[(bitsize-1):0];
//Inputs & Outputs of basecells
reg [(bitsize-1):0] f1_i[(bitsize-1):0];
reg [(bitsize-1):0] f2_i[(bitsize-1):0];
reg [(bitsize-1):0] b_i[(bitsize-1):0];
reg [(bitsize-1):0] c_i[(bitsize-1):0];
wire [(bitsize-1):0] sum_o[(bitsize-1):0];
wire [(bitsize-1):0] c_o[(bitsize-1):0];
integer i, j; //for rewiring loops
genvar k, l;
//Generate basecell modules
generate
for (k = 0; k < bitsize; k = k + 1)
begin
for (l = 0; l < bitsize; l = l + 1)
begin : basecell
basecell_fa bscll(f1_i[k][l], f2_i[k][l], b_i[k][l], c_i[k][l], sum_o[k][l], c_o[k][l]);
end
end
endgenerate
always@*
begin
//Wire renameing for basecell ports
for(i = 0; i < bitsize; i = i + 1)
begin
for(j = 0; j < bitsize; j = j + 1)
begin
f1_i[i][j] = a[i];
f2_i[i][j] = b[j];
if(i == 0)
begin
b_i[i][j] = 1'b0;
end
else if(j == (bitsize - 1))
begin
b_i[i][j] = carry_vec[i-1][j]; //note: j = bitsize - 1
end
else
begin
b_i[i][j] = sum_vec[i-1][j+1];
end
if(j == 0)
begin
c_i[i][j] = 1'b0;
end
else
begin
c_i[i][j] = carry_vec[i][j-1];
end
sum_vec[i][j] = sum_o[i][j];
carry_vec[i][j] = c_o[i][j];
end
end
//Output wire renameing
for(i = 0; i < bitsize; i = i + 1)
begin
p[i] = sum_vec[i][0];
end
for(i = 1; i < bitsize; i = i + 1)
begin
p[bitsize+i-1] = sum_vec[bitsize-1][i];
end
p[(2*bitsize)-1] = carry_vec[bitsize-1][bitsize-1];
end
endmodule // Parameterized Carry Save Multiplier
module basecell_ha(f1_i, f2_i, b_i, sum_o, c_o);
input f1_i, f2_i, b_i;
output sum_o, c_o;
wire pp;
assign pp = f1_i & f2_i;
HA adder(pp, b_i, sum_o, c_o);
endmodule
module basecell_fa(f1_i, f2_i, b_i, c_i, sum_o, c_o);
input f1_i, f2_i, b_i, c_i;
output sum_o, c_o;
wire pp;
assign pp = f1_i & f2_i;
FA adder(pp, b_i, c_i, sum_o, c_o);
endmodule
module FA(A, B, Cin, S, Cout);
input A, B, Cin;
output S, Cout;
wire ha_sum;
assign ha_sum = A ^ B;
assign S = ha_sum ^ Cin; //Sum
assign Cout = (A & B) | (ha_sum & Cin);
endmodule
module HA(A, B, S, Cout);
input A, B;
output S, Cout;
assign S = A ^ B;
assign Cout = A & B;
endmodule
Quartus RTL图
Quartus Post mapping图
阵列乘法器
- 阵列乘法器由多个加法器和乘法器组成,其工作原理是将待乘数和乘数分别按位拆分成多个小段,在阵列乘法器中进行乘法运算和累加,并最终得出乘积。
- 阵列乘法器将待乘数和乘数逐位相乘,并将结果送入加法器进行累加。在每个时钟周期中,阵列乘法器会对每个乘数位进行一次部分积计算,直到所有位都完成运算。部分积计算完成后,阵列乘法器会将结果根据位数进行排序和加和,以得到最终乘积。
- 阵列乘法器的优点是速度快、复杂度低、结构简单等。它广泛应用于数字信号处理、数值计算、图像处理等领域中,是实现大规模数据处理的重要工具。
Verilog代码:
//-------------------------------------------------------------------------
// Copyright (c) 2014-2023 All rights reserved
// -----------------------------------------------------------------------------
// Author : HFUT90S 1320343336@qq.com
// File : array_mult.v
// Create : 2023-03-22 11:10:13
// Revise : 2023-03-22 11:10:13
// Editor : 0078
// Verdion: v1.0
// Description: array multiplier
// -----------------------------------------------------------------------------
module array_mult(
input [7:0] A, // 8 位被乘数
input [7:0] B, // 8 位乘数
output reg [15:0] P // 16 位乘积
);
wire [15:0] partial_Ps [7:0]; // 存储部分积的数组
integer j;
// 生成部分积
generate
genvar i;
for (i=0; i<8; i=i+1) begin
assign partial_Ps[i] = A * (B[i]<<i); // 计算第 i 个部分积
end
endgenerate
// 计算乘积
always @(*) begin
P = 16'd0; // 初始化乘积为 0
// 将所有部分积相加
for (j=0; j<8; j=j+1) begin
P = P + partial_Ps[j];
end
end
endmodule
Quartus RTL图
Quartus Post mapping图
Multiplier Test Bench
很多同学私信想要的tb文件他来了,包含上述所有乘法器的tb哈;
ps:此系列只是科普类博文,感兴趣的同学建议继续学习相关乘法器结构和相关算法;
不建议在设计中使用这里提到的verilog设计,而是尝试调用成熟IP。
module Multiplier_tb ();
reg [7:0] A, B;
reg signed [7:0] A1, B1;
wire [15:0] P1;
wire [15:0] P2;
wire [15:0] P3;
wire [15:0] P4;
wire [15:0] P5;
wire signed [15:0] P6;
wire [15:0] P7;
wire [15:0] P8;
wire [15:0] P9;
///1
para_mult #(.N(8)) inst_para_mult (.a(A), .b(B), .out(P1));
///2
shift_add_mult inst_shift_add_mult (.operand1(A), .operand2(B), .result(P2));
///3
shift_add_mult1 inst_shift_add_mult1 (.a(A), .b(B), .p(P3));
///4
lut_mult inst_lut_mult (.a(A), .b(B), .p(P4));
///5
add_tree_mult inst_add_tree_mult (.a(A), .b(B), .out(P5));
///6
booth_mult inst_booth_mult (.a(A1), .b(B1), .p(P6));
///7
wallace_mult inst_wallace_mult (.A(A), .B(B), .P(P7));
///8
carry_save_mult inst_carry_save_mult (.a(A), .b(B), .p(P8));
///9
array_mult inst_array_mult (.A(A), .B(B), .P(P9));
integer i;
//1
integer para_mult_pass_count = 0;
integer para_mult_fail_count = 0;
//2
integer shift_add_mult_pass_count = 0;
integer shift_add_mult_fail_count = 0;
//3
integer shift_add_mult1_pass_count = 0;
integer shift_add_mult1_fail_count = 0;
//4
integer lut_mult_pass_count = 0;
integer lut_mult_fail_count = 0;
//5
integer add_tree_mult_pass_count = 0;
integer add_tree_mult_fail_count = 0;
//6
integer booth_mult_pass_count = 0;
integer booth_mult_fail_count = 0;
//7
integer wallace_mult_pass_count = 0;
integer wallace_mult_fail_count = 0;
//8
integer carry_save_mult_pass_count = 0;
integer carry_save_mult_fail_count = 0;
//9
integer array_mult_pass_count = 0;
integer array_mult_fail_count = 0;
initial begin
for (i = 0; i < 257; i = i + 1) begin
// 设置输入数据
A = $urandom_range(0, 255);
B = $urandom_range(0, 255);
// 等待一些时间
A1 = $urandom_range(0, 255);
B1 = $urandom_range(0, 255);
#40
// 验证输出结果
//1
if (P1 == A * B) begin
//$display("Test Passed: Output matches expected result");
para_mult_pass_count = para_mult_pass_count + 1;
end
else begin
//$display("Test Failed: Output does not match expected result");
para_mult_fail_count = para_mult_fail_count + 1;
end
// 2
if (P2 == A * B) begin
//$display("Test Passed: Output matches expected result");
shift_add_mult_pass_count = shift_add_mult_pass_count + 1;
end
else begin
//$display("Test Failed: Output does not match expected result");
shift_add_mult_fail_count = shift_add_mult_fail_count + 1;
end
//3
if (P3 == A * B) begin
//$display("Test Passed: Output matches expected result");
shift_add_mult1_pass_count = shift_add_mult1_pass_count + 1;
end
else begin
//$display("Test Failed: Output does not match expected result");
shift_add_mult1_fail_count = shift_add_mult1_fail_count + 1;
end
//4
if (P4 == A * B) begin
//$display("Test Passed: Output matches expected result");
lut_mult_pass_count = lut_mult_pass_count + 1;
end
else begin
//$display("Test Failed: Output does not match expected result");
lut_mult_fail_count = lut_mult_fail_count + 1;
end
//5
if (P5 == A * B) begin
//$display("Test Passed: Output matches expected result");
add_tree_mult_pass_count = add_tree_mult_pass_count + 1;
end
else begin
//$display("Test Failed: Output does not match expected result");
add_tree_mult_fail_count = add_tree_mult_fail_count + 1;
end
//6
if (P6 == A1 * B1) begin
//$display("Test Passed: Output matches expected result");
booth_mult_pass_count = booth_mult_pass_count + 1;
end
else begin
booth_mult_fail_count = booth_mult_fail_count + 1;
end
//7
if (P7 == A * B) begin
//$display("Test Passed: Output matches expected result");
wallace_mult_pass_count = wallace_mult_pass_count + 1;
end
else begin
//$display("Test Failed: Output does not match expected result");
wallace_mult_fail_count = wallace_mult_fail_count + 1;
end
//8
if (P8 == A * B) begin
//$display("Test Passed: Output matches expected result");
carry_save_mult_pass_count = carry_save_mult_pass_count + 1;
end
else begin
//$display("Test Failed: Output does not match expected result");
carry_save_mult_fail_count = carry_save_mult_fail_count + 1;
end
//9
if (P9 == A * B) begin
//$display("Test Passed: Output matches expected result");
array_mult_pass_count = array_mult_pass_count + 1;
end
else begin
//$display("Test Failed: Output does not match expected result");
array_mult_fail_count = array_mult_fail_count + 1;
end
end
//Multiplier Coverage Report
$display("//============Multiplier Coverage Report============//");
$display("Total Tests: %d", i);
//1
$display("//====================================//");
$display("para_mult Passed: %d", para_mult_pass_count);
$display("para_mult Failed: %d", para_mult_fail_count);
$display("//====================================//");
//2
$display("//====================================//");
$display("shift_add_mult Passed: %d", shift_add_mult_pass_count);
$display("shift_add_mult Failed: %d", shift_add_mult_fail_count);
$display("//====================================//");
//3
$display("//====================================//");
$display("shift_add_mult1 Passed: %d", shift_add_mult1_pass_count);
$display("shift_add_mult1 Failed: %d", shift_add_mult1_fail_count);
$display("//====================================//");
//4
$display("//====================================//");
$display("lut_mult Passed: %d", lut_mult_pass_count);
$display("lut_mult Failed: %d", lut_mult_fail_count);
$display("//====================================//");
//5
$display("//====================================//");
$display("add_tree_mult Passed: %d", add_tree_mult_pass_count);
$display("add_tree_mult Failed: %d", add_tree_mult_fail_count);
$display("//====================================//");
//6
$display("//====================================//");
$display("booth_mult Passed: %d", booth_mult_pass_count);
$display("booth_mult Failed: %d", booth_mult_fail_count);
$display("//====================================//");
//7
$display("//====================================//");
$display("wallace_mult Passed: %d", wallace_mult_pass_count);
$display("wallace_mult Failed: %d", wallace_mult_fail_count);
$display("//====================================//");
//8
$display("//====================================//");
$display("carry_save_mult Passed: %d", carry_save_mult_pass_count);
$display("carry_save_mult Failed: %d", carry_save_mult_fail_count);
$display("//====================================//");
//9
$display("//====================================//");
$display("array_mult Passed: %d", array_mult_pass_count);
$display("array_mult Failed: %d", array_mult_fail_count);
$display("//====================================//");
// 打印测试通过和测试失败的总数
//$display("Total Tests: %d", i);
//$display("Passed: %d", pass_count);
//$display("Failed: %d", fail_count);
if ( para_mult_fail_count == 0 && shift_add_mult_fail_count ==0 && shift_add_mult1_fail_count ==0
&& lut_mult_fail_count == 0 && add_tree_mult_fail_count == 0 && booth_mult_fail_count == 0
&& wallace_mult_fail_count == 0 && carry_save_mult_fail_count == 0 && array_mult_fail_count ==0
) begin
$display("//================== All Multipliers Random Test PASS!!==================//");
$display("//============================== Have a good day!!===========================//");
end
else begin
$display("//================== Existence Multiplier Random Test Fail!!==================//");
$display("//============================<< Pls Debug >>==============================//");
end
// 结束仿真
$finish;
end
endmodule
总结
随着计算机科学技术的不断发展,乘法器在数字信号处理、计算机控制等领域中被广泛应用。而不同类型的乘法器,都有各自的优点和适用范围。
-
并行乘法器:
并行乘法器是采用多个乘法器并联运算的方式,通过并行处理提高了乘法器的速度。该乘法器的速度快,适用于高速乘法运算,但其硬件开销较高,面积较大。 -
移位相加乘法器:
移位相加乘法器采用移位和加法操作实现乘法运算,不需要乘法器,硬件实现简单。但是,其速度较慢,适用于数字信号处理等低速乘法运算。 -
查找表乘法器:
查找表乘法器通过预先计算出乘积表,将乘积表存储在查找表中,乘法运算时查表即可。该乘法器的速度较快,适用于低精度的乘法运算。 -
加法树乘法器:
加法树乘法器采用加法器来相加乘积,能够高效地实现数据的并行处理和链式计算。其硬件复杂度和面积都较小,适用于芯片上集成乘法器的应用。 -
Booth乘法器:
Booth乘法器采用编码和位移混合计算的方式,有效减少了乘法器中的运算步骤,进而提高乘法器的速度和精度。适用于高精度运算、数字信号处理等领域。 -
Wallace树乘法器:
Wallace树乘法器是一种高效的乘法器结构,通过对相邻的部分积进行压缩,减少了乘法器中的加法器数量,从而达到减小硬件成本、提高速度的目的。适用于高速计算并且不要求过高的精度场合。 -
Carry-save乘法器:
Carry-save乘法器通过将乘积转化为进位和不进位两部分,分别存储、处理,减少了加法器的数量,提高了乘法器的速度和并行性。适用于高速计算的场合,如图像处理、信号分析等。 -
阵列乘法器:
阵列乘法器是一种多行多列的矩阵结构,采用并行处理的方式实现乘法运算。其硬件成本较低,适用于数字信号处理、图像处理、通信等领域。文章来源:https://www.toymoban.com/news/detail-455188.html
在选择乘法器的类型时,需要根据具体的应用场景和需求来选择,以达到最佳的性能和适用范围。文章来源地址https://www.toymoban.com/news/detail-455188.html
到了这里,关于数字IC经典电路(2)——经典乘法器的实现(乘法器简介及Verilog实现)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!