数字IC经典电路(2)——经典乘法器的实现(乘法器简介及Verilog实现)

写在前面的话

数字电路中乘法器是一种常见的电子元件,其基本含义是将两个数字相乘,并输出其乘积。与加法器不同,乘法器可以实现更复杂的运算,因此在数字电路系统中有着广泛的应用。

乘法器的主要用途是在数字信号处理、计算机科学以及其他数字电路应用中进行精确的数字乘法运算。例如,在数字信号处理中,乘法器通常用于数字滤波器中的系数乘法;在计算机科学中,它们被用于执行浮点运算;而在其他数字电路应用中,乘法器也可以实现时域和频域的变换等。

在数字电路系统中,乘法器的重要性无法被忽视。它们不仅能够提高数字信号处理和计算机科学中的运算速度和精度,还能够实现更复杂的数字信号处理和分析。此外,乘法器还可以优化数字信号处理的功耗和面积,从而使得数字电路系统具有更优异的性能。

数字电路乘法器在现代电子技术中扮演着重要的角色。它们为数字信号处理、计算机科学以及其他数字电路应用提供了精确的数字乘法运算,并且可以进一步优化数字电路系统的性能,使其具有更高的效率和可靠性。随着人们对数字信号处理和计算机科学的需求不断增加,乘法器将继续发挥着重要的作用,为数字电路应用提供更好的解决方案。

乘法器分类

在数字集成电路中,乘法器的种类主要可以分为以下三类:

  1. 串行乘法器:这种乘法器是最简单的乘法器之一,它将两个数的乘积通过一个计数器逐位相加得到。由于每一位的运算都需要消耗时间,因此串行乘法器的速度较慢。
  2. 并行乘法器:与串行乘法器不同,这种乘法器可以同时对多位进行运算,因此运算速度更快。常见的并行乘法器有布斯-加勒德算法和沃勒斯算法等。
  3. 快速乘法器:这种乘法器采用了更高级的算法,在乘法过程中将输入的数按位划分,并尝试最小化运算次数。常见的快速乘法器包括批处理器算法、底数为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. 将乘数的每个二进制位从低位到高位逐个取出,如果该位为1,则将被乘数左移相应的位数后与部分积相加。
  3. 重复第2步,直到乘数的所有二进制位都处理完毕,此时得到的部分积即为最终结果。

例如,假设要计算5×6,首先将5和6转换为二进制数101和110,然后从低位到高位依次处理乘数的每个二进制位,得到以下过程:

  • 当处理乘数的最低位0时,将被乘数5左移0位后加到部分积中,即得到部分积000。
  • 当处理乘数的次低位1时,不需要加入任何东西,部分积保持不变,仍为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增长速度较快,在多位乘法器上实现时需要花费较多的硬件成本。

在这里插入图片描述

加法树乘法器

结合移位相加乘法器和查找表乘法器的优点。
加法树乘法器是一种并行的硬件电路设计,主要用于实现高效的乘法运算。它采用树状结构的加法器来实现多项式乘法,且不需要显式地计算进位或借位。该乘法器的输入是两个被乘数,输出是它们的乘积。

加法树乘法器的实现过程主要分为三步:

  1. 生成部分积:将两个被乘数分别拆分为若干位二进制数,按照乘法规则计算每一位上的部分积,得到一个二维矩阵。

  2. 生成加法树:对于每一列的部分积,采用二路并行加法器进行相加,得到一列的加和结果。接着对这些列的加和结果进行二路并行相加,最后得到一个总的加和结果。

  3. 输出乘积:将最后的加和结果转换为二进制数形式,得到两个被乘数的乘积。
    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,然后利用乘法和加法运算,对每一位进行计算。具体步骤如下:

  1. 对第二个乘数进行编码:将每个位与其右侧相邻位比较,如果它们的值相同,就将该位编码为0;否则,将其编码为1或-1。最右侧位的右侧补0。

  2. 将第一个乘数左移一位,形成一个n+1位的数。同时,将编码后的第二个乘数左移一位。

  3. 对于从右向左的每个位,进行以下操作:

  • 如果该位和前一位相同,就不进行任何操作。

  • 如果该位是1且前一位是0(或该位是0且前一位是1),则将第一个乘数加上(或减去)第二个乘数。

  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

在这里插入图片描述

总结

随着计算机科学技术的不断发展,乘法器在数字信号处理、计算机控制等领域中被广泛应用。而不同类型的乘法器,都有各自的优点和适用范围。

  1. 并行乘法器:
    并行乘法器是采用多个乘法器并联运算的方式,通过并行处理提高了乘法器的速度。该乘法器的速度快,适用于高速乘法运算,但其硬件开销较高,面积较大。

  2. 移位相加乘法器:
    移位相加乘法器采用移位和加法操作实现乘法运算,不需要乘法器,硬件实现简单。但是,其速度较慢,适用于数字信号处理等低速乘法运算。

  3. 查找表乘法器:
    查找表乘法器通过预先计算出乘积表,将乘积表存储在查找表中,乘法运算时查表即可。该乘法器的速度较快,适用于低精度的乘法运算。

  4. 加法树乘法器:
    加法树乘法器采用加法器来相加乘积,能够高效地实现数据的并行处理和链式计算。其硬件复杂度和面积都较小,适用于芯片上集成乘法器的应用。

  5. Booth乘法器:
    Booth乘法器采用编码和位移混合计算的方式,有效减少了乘法器中的运算步骤,进而提高乘法器的速度和精度。适用于高精度运算、数字信号处理等领域。

  6. Wallace树乘法器:
    Wallace树乘法器是一种高效的乘法器结构,通过对相邻的部分积进行压缩,减少了乘法器中的加法器数量,从而达到减小硬件成本、提高速度的目的。适用于高速计算并且不要求过高的精度场合。

  7. Carry-save乘法器:
    Carry-save乘法器通过将乘积转化为进位和不进位两部分,分别存储、处理,减少了加法器的数量,提高了乘法器的速度和并行性。适用于高速计算的场合,如图像处理、信号分析等。

  8. 阵列乘法器:
    阵列乘法器是一种多行多列的矩阵结构,采用并行处理的方式实现乘法运算。其硬件成本较低,适用于数字信号处理、图像处理、通信等领域。

在选择乘法器的类型时,需要根据具体的应用场景和需求来选择,以达到最佳的性能和适用范围。
在这里插入图片描述

  • 59
    点赞
  • 261
    收藏
    觉得还不错? 一键收藏
  • 14
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值