乘法器的实现(阵列、Booth、Wallace)


1 乘法器

M和N位宽输入的乘法,采用一个N加法器需要M个周期。

利用移位和相加将M个部分积(partial product)加在一起。部分积的计算位相乘本质上是与逻辑。

         101010       被乘数
      ×    1011       乘数
      ———————————
         101010   \
        101010     |  部分积
       000000      |
    + 101010      /
    —————————————
      111001110

2 部分积的产生

2.1 波兹(Booth)编码

乘数 8’b0111_1110 可以转换成 8’b1000_0000 - 8’b0000_0010 。这里用8’b1000_00I0 表示(I表示 -1 )。

这可以减少非0行的数量,使得部分积的数目至少可以减少原来的一半。部分积数目的减少意味着相加次数的减少,从而加快了运算速度并减少了面积。

保证了在每两个连续位中最多只有一个是 1 或者 -1 。形式上相当于把乘数变换成一个四进制形式。8’b1000_00I0 = (2,0,0,-2)(四进制)

问题:与{0,1}相乘等效于AND,但是与{-2,-1,0,1,2}相乘还需要反向逻辑和移位逻辑,大小不同的部分积阵列对乘法器设计不合理。

2.2 改进的波兹编码

改进的波兹编码(modified Booth’s recoding)乘数由最高有效位(msb)到最低有效位(lsb)进行,按3位一组进行划分,相互重叠一位,编码表:

乘数位编码位编码
000000
001+被乘数01
010+被乘数01
011+2×被乘数10
100-2×被乘数I0
101-被乘数0I
110-被乘数0I
111000

本质是从msb到lsb检查乘数中1的字串,用一个以1开头或以-1结尾的字符串代替他们。
例子:
011 :一串1的开始,所以用一个开头的1代替(100)
110 :一串1的结尾,所以用一个结尾的-1代替(0I0)

8’b0111_1110,从msb到lsb,分成3位一组首尾重叠的4组:01(1),11(1),11(1),10(0)。编码后为10,00,00,I0,这与上述的编码是吻合的。

3 部分积的累加

对部分积相加是一个多操作数的加法,一个直接累加的部分积方法是用许多加法器形成阵列,所以被称为 阵列乘法器(array multiplier)。

一个更为先进的方法与树结构的形式完成加法。

3.1 阵列乘法器

在这里插入图片描述

AND 门产生部分积,加法器阵列实现相加。

在这里插入图片描述

所有关键路径都具有相同的长度。

上述乘法器只能进行无符号数相乘。

3.2 进位保留乘法器

如果进位向下沿而不是向左,可以得到一个更有效的实现。进位不是立即相加,而是传递给下一级加法器,在最后一级在一个快速进位传播(如超前进位)加法器中合并。

代价:需要一个额外的加法器,被称为向量合并(vector-merging)加法器。由此得到的乘法器被称为进位保留乘法器。

优点:在最坏情形下关键路径最短且唯一确定。

拓扑优化后的结构:

在这里插入图片描述

这种结构可以更好的映射到硅片上。

3.3 Wallace 树形乘法器

部分积加法器可以设计成树形,可以减少关键路径和所需的加法器单元数目。

在这里插入图片描述

上面的4个4位部分积,只有第3bit需要加 4 个。

第一步,第3列和低4列引入2个半加器,如图b所示,压缩后得到图c。
第二步,第3、4、5列引入3个全加器,第2列引入1个半加器,得到图d。
第三步,使用简单的两输入加法器。

前两步一共使用了3个全加器、3个半加器。原来的进位保留乘法器结构需要6个全加器、6个半加器。

全加器3个输入两个输出,所以运算过程中又称为压缩器。

优点:

  • 节省了较大乘法器所需硬件,减少了传播延时。

缺点:

  • 不规则,高质量版图设计任务变复杂。

4 Verilog 实现

4.1 普通阵列乘法器

以 8bit 无符号数相乘为例,注意这里的设计没有考虑性能。

array_multiplier.v:

`timescale 1ns/1ps  

module array_multiplier ( 
    input               I_sys_clk,
    input               I_reset_n,
    input               I_valid,
    input      [7:0]    I_a,
    input      [7:0]    I_b,
    output reg          O_valid,
    output reg [15:0]   O_c
);
                
//--- Main body of code ---  
always @(posedge I_sys_clk or negedge I_reset_n)
begin
    if(~I_reset_n) 
    begin
        O_valid <= 1'b0;
        O_c     <= 16'b0;
    end 
    else
    begin
        O_valid <= I_valid;
        O_c     <= (({8{I_b[0]}} & I_a)     ) + 
                   (({8{I_b[1]}} & I_a) << 1) + 
                   (({8{I_b[2]}} & I_a) << 2) + 
                   (({8{I_b[3]}} & I_a) << 3) + 
                   (({8{I_b[4]}} & I_a) << 4) + 
                   (({8{I_b[5]}} & I_a) << 5) + 
                   (({8{I_b[6]}} & I_a) << 6) + 
                   (({8{I_b[7]}} & I_a) << 7)  
                   ;
    end
end

endmodule 

testbench array_multiplier_tb.sv:

`timescale 1ns/1ps

module array_multiplier_tb();

parameter T = 5;  

reg           I_sys_clk;
reg           I_reset_n;
reg           I_valid  ;
reg  [7:0]    I_a      ;
reg  [7:0]    I_b      ;
wire          O_valid  ;
wire [15:0]   O_c      ;


initial begin
    I_sys_clk <= 'b0;
    I_reset_n <= 'b0;
    I_valid   <= 'b0; 
    I_a       <= 'b0; 
    I_b       <= 'b0; 
    #(T*20)  
    I_reset_n <= 'b1;
    #(T*20)  
    data_gen();
    $finish();
end

always #(T/2) I_sys_clk <= ~I_sys_clk;


array_multiplier array_multiplier_u ( 
    .I_sys_clk(I_sys_clk),
    .I_reset_n(I_reset_n),
    .I_valid  (I_valid  ),
    .I_a      (I_a      ),
    .I_b      (I_b      ),
    .O_valid  (O_valid  ),
    .O_c      (O_c      )
);

task data_gen();
    for (int i = 0; i < 256; i++) begin
        I_valid   <= 'b1; 
        I_a       <= i; 
        I_b       <= i;
        $display("%d x %d = %d", i, i, i*i);
        @(posedge I_sys_clk);
        I_valid   <= 'b0;
    end
endtask

endmodule

sim.do 文件:

cd D:/prj/modelsim_prj/multiplier/array_multiplier/ 
vlib work  
vlog array_multiplier.v  array_multiplier_tb.sv 
vsim -novopt work.array_multiplier_tb 

sim.bat 文件:

vsim -do sim.do

所有文件放在相同路径下,双击批处理文件即可开始Modelsim仿真。

仿真结果:
在这里插入图片描述

4.2 Booth 乘法器

Verilog 设计,注意,这个代码只是描述算法,需要进行符号位拓展、乘换成与逻辑、以及将乘2的幂转换为移位处理。

`timescale 1ns/1ps

module booth_multiplier (
    input               I_sys_clk,
    input               I_reset_n,
    input               I_valid  ,
    input      [7:0]    I_a      ,
    input      [7:0]    I_b      ,
    output reg          O_valid  ,
    output reg [15:0]   O_c
);

//--- Main body of code ---  
wire [8:0] W_b;
wire [15:0] W_a_n;
wire [2:0] W_booth_code_pre [0:3];
reg       R_valid                ;
reg signed [15:0] R_partial_product [0:3];

assign W_b = {I_b, 1'b0};
assign W_a_n = -{{8{I_a[7]}}, I_a};

genvar gen_i;
for (gen_i = 0; gen_i < 4; gen_i = gen_i + 1) begin 
    assign W_booth_code_pre[gen_i] = W_b[gen_i*2 +: 3];
    always @(posedge I_sys_clk or negedge I_reset_n)
    begin
        if(~I_reset_n) 
        begin
            R_partial_product[gen_i] <= 1'b0;
        end 
        else
        begin
            case (W_booth_code_pre[gen_i])
                3'b000: R_partial_product[gen_i] <= 9'b0;
                3'b001: R_partial_product[gen_i] <= $signed(I_a) *  1 * 2**(gen_i*2) ;
                3'b010: R_partial_product[gen_i] <= $signed(I_a) *  1 * 2**(gen_i*2) ;
                3'b011: R_partial_product[gen_i] <= $signed(I_a) *  2 * 2**(gen_i*2) ;
                3'b100: R_partial_product[gen_i] <= $signed(I_a) * -2 * 2**(gen_i*2) ;
                3'b101: R_partial_product[gen_i] <= $signed(I_a) * -1 * 2**(gen_i*2) ;
                3'b110: R_partial_product[gen_i] <= $signed(I_a) * -1 * 2**(gen_i*2) ;
                3'b111: R_partial_product[gen_i] <= 9'b0;
                default: R_partial_product[gen_i] <= 9'b0;
            endcase
        end
    end
end

always @(posedge I_sys_clk or negedge I_reset_n)
begin
    if(~I_reset_n) 
    begin
        R_valid <= 1'b0;
    end 
    else
    begin
        R_valid <= I_valid;
    end
end

always @(posedge I_sys_clk or negedge I_reset_n)
begin
    if(~I_reset_n) 
    begin
        O_valid <= 1'b0;
        O_c     <= 16'b0;
    end 
    else
    begin
        O_valid <= R_valid;
        O_c     <= R_partial_product[0] + 
                   R_partial_product[1] + 
                   R_partial_product[2] + 
                   R_partial_product[3]  
                   ;
    end
end

endmodule

testchech:

`timescale 1ns/1ps

module booth_multiplier_tb();

parameter T = 5;  

reg           I_sys_clk;
reg           I_reset_n;
reg           I_valid  ;
reg  [7:0]    I_a      ;
reg  [7:0]    I_b      ;
wire          O_valid  ;
wire [15:0]   O_c      ;

// reference model signal
reg [7:0]    R_i ;
reg [7:0]    R1_i ;
reg [7:0]    R2_i ;

initial begin
    I_sys_clk <= 'b0;
    I_reset_n <= 'b0;
    I_valid   <= 'b0; 
    I_a       <= 'b0; 
    I_b       <= 'b0; 
    #(T*20)  
    I_reset_n <= 'b1;
    #(T*20)  
    data_gen();
    $finish();
end

always #(T/2) I_sys_clk <= ~I_sys_clk;


booth_multiplier booth_multiplier_u ( 
    .I_sys_clk(I_sys_clk),
    .I_reset_n(I_reset_n),
    .I_valid  (I_valid  ),
    .I_a      (I_a      ),
    .I_b      (I_b      ),
    .O_valid  (O_valid  ),
    .O_c      (O_c      )
);


task data_gen();
    for (int i = 0; i < 256; i++) begin
        I_valid   <= 'b1; 
        I_a       <= i; 
        I_b       <= i;
        R_i <= i;
        R1_i <= R_i;
        R2_i <= R1_i;
        // reference model
        if (O_valid)
        begin
            $display("i=%d, O_c=%d, check=%d", $signed(R2_i), $signed(O_c), $signed(O_c) == $signed(R2_i) * $signed(R2_i));
        end
        @(posedge I_sys_clk);
        I_valid   <= 'b0;
    end
endtask

endmodule

Modilsim do文件做对应修改,sim.do:

cd D:/prj/modelsim_prj/multiplier/booth_multiplier/ 
vlib work  
vlog booth_multiplier.v  booth_multiplier_tb.sv 
vsim -novopt work.booth_multiplier_tb 

仿真结果:

在这里插入图片描述

4.3 Wallace 乘法器

以4bit×4bit为例,按照结构把线连起来就行:
wallace_multiplier.v:

`timescale 1ns/1ps

module wallace_multiplier (
    input               I_sys_clk,
    input               I_reset_n,
    input               I_valid  ,
    input      [3:0]    I_a      ,
    input      [3:0]    I_b      ,
    output reg          O_valid  ,
    output reg [7:0]    O_c
);

reg       R_valid ;
reg [3:0] R_partial_product [0:3];  

wire [1:0] W_level1_c,W_level1_carry;
wire [3:0] W_level2_c,W_level2_carry;
wire [6:0] W_level3[0:1];

genvar gen_i;
generate
    for (gen_i = 0; gen_i < 4; gen_i = gen_i + 1) begin
        always @(posedge I_sys_clk or negedge I_reset_n)
        begin
            if(~I_reset_n) 
            begin
                R_partial_product[gen_i] = 4'd0;
            end 
            else
            begin
                R_partial_product[gen_i] = {4{I_b[gen_i]}} & I_a;
            end
        end
    end
endgenerate

// level1
adder_half adder_half_u1 (
    .I_a    (R_partial_product[2][1]),
    .I_b    (R_partial_product[3][0]),
    .O_c    (W_level1_c[0]),
    .O_carry(W_level1_carry[0])
); 
adder_half adder_half_u2 (
    .I_a    (R_partial_product[1][3]),
    .I_b    (R_partial_product[2][2]),
    .O_c    (W_level1_c[1]),
    .O_carry(W_level1_carry[1])
);


// level2
adder_half adder_half_u3 (
    .I_a    (R_partial_product[1][1]),
    .I_b    (R_partial_product[2][0]),
    .O_c    (W_level2_c[0]    ),
    .O_carry(W_level2_carry[0])
);
adder adder_u1 (
    .I_a     (R_partial_product[0][3]),
    .I_b     (R_partial_product[1][2]),
    .I_carry (W_level1_c[0]          ),
    .O_c     (W_level2_c[1]          ),
    .O_carry (W_level2_carry[1]      )
);
adder adder_u2 (
    .I_a     (R_partial_product[1][3]),
    .I_b     (W_level1_c[1]          ),
    .I_carry (W_level1_carry[0]      ),
    .O_c     (W_level2_c[2]          ),
    .O_carry (W_level2_carry[2]      )
);
adder adder_u3 (
    .I_a     (R_partial_product[2][3]),
    .I_b     (R_partial_product[3][2]),
    .I_carry (W_level1_carry[1]       ),
    .O_c     (W_level2_c[3]          ),
    .O_carry (W_level2_carry[3]      )
);

assign W_level3[0] = {R_partial_product[3][3], W_level2_c[3:1], R_partial_product[0][2:0]};
assign W_level3[1] = {W_level2_carry[3:0], W_level2_c[0], R_partial_product[1][0], 1'b0};

always @(posedge I_sys_clk or negedge I_reset_n)
begin
    if(~I_reset_n) 
    begin
        R_valid <= 1'b0;
        O_valid <= 1'd0;
        O_c     <= 8'd0;
    end 
    else
    begin
        R_valid <= I_valid;
        O_valid <= R_valid;
        O_c     <= W_level3[0] + W_level3[1]; 
                   
    end
end

endmodule

module adder_half (
    input  I_a,
    input  I_b,
    output O_c,
    output O_carry
);

assign O_c = I_a ^ I_b;
assign O_carry = I_a & I_b;

endmodule

module adder (
    input  I_a,
    input  I_b,
    input  I_carry,
    output O_c,
    output O_carry
);

assign O_c = I_a ^ I_b ^ I_carry;
assign O_carry = (I_a & I_b) | ((I_a ^ I_b) & I_carry);

endmodule

仿真结果:

在这里插入图片描述

5 总结

数字集成电路设计可能对加法器的面积和版图有较高的要求。对于FPGA设计,可能基本阵列加法器用着还更简单顺手。主要看的的题目可能问的是与一个常数相乘的设计,这个时候要参考 Booth 编码的思想,因为不是实时乘数的话,预编码之后确实还是节省资源的。

参考

数字集成电路-电路、系统与设计(第二版)

  • 16
    点赞
  • 101
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
### 回答1: booth wallace乘法器是一种用于进行二进制乘法的数字电路。该乘法器的设计目标是提高乘法的速度和效率。 booth wallace乘法器采用了一种称为booth算法的技术。该算法通过将乘数的二进制表示进行编码,将连续的1和0序列转换为加减法操作,从而减少了乘法操作的次数。 booth wallace乘法器分为三个主要阶段:编码阶段、部分积生成阶段和最终求和阶段。 在编码阶段,乘数被编码为一系列编码位,表示了乘数的二进制表达中连续的1和0序列。编码过程中的加减法操作会生成高位部分积(high partial product)和低位部分积(low partial product)。 在部分积生成阶段,根据编码位的值,选择性地将高位部分积左移或右移,并与原值相加,得到最终的高位部分积。低位部分积则通过左移操作得到。 最后,在最终求和阶段,对所有的部分积进行相加,得到最终的乘积。 booth wallace乘法器相比传统的乘法器具有更快的操作速度和更高的效率。该乘法器适用于需要进行大量乘法运算的应用场景,例如数字信号处理、图形处理和通信系统等。 尽管booth wallace乘法器在提高速度和效率方面有优势,但其复杂的电路结构和额外的编码操作也会增加硬件成本和功耗。因此,在具体应用中需要综合考虑设计需求和成本效益来选择是否使用booth wallace乘法器。 ### 回答2: Booth Wallace乘法器是一种用于计算两个二进制数的乘积的算法和电路。它采用了一种改进的二进制乘法算法,可以更高效地进行数学运算。 Booth Wallace乘法器通过将乘法运算拆分为多个部分来减少计算所需的步骤。该乘法器使用了3位补偿的形式,通过连续进行两次移位操作,将乘法转化为部分乘积的加法和减法。 具体来说,该乘法器首先对乘数和被乘数进行了扩展,以便处理负数的乘法。然后,它使用一个控制信号,根据乘数的当前位和前一位的值来判断应该进行加法还是减法。 在计算过程中,乘数的每一位都会遍历。如果乘数的当前位和前一位的值相同,那么该位的值保持不变。如果它们不同,那么该位的值将根据乘数的前一位是0还是1的情况,进行加法或减法。 最后,通过对部分乘积进行累加,我们可以得到最终的乘积结果。 与传统的乘法算法相比,Booth Wallace乘法器可以减少乘法运算所需的步骤,并且在某些情况下可以显著提高计算效率。这种乘法器在数字信号处理器(DSP)和其他需要高效乘法运算的应用中得到广泛应用。 ### 回答3: Booth Wallace乘法器是一种常用于计算机数字逻辑电路中的乘法器。它是由Andrew D. Booth和Wallace Van Slyke发明的,用于在计算机中进行定点乘法运算。 Booth Wallace乘法器利用了二进制数的补码表示方法来进行乘法计算。其基本原理是将乘法运算转化为一系列的加法运算。它通过将乘数拆分为一串几个连续的1和0的比特位组合,然后将被乘数与这些比特位进行相应的位移和相加操作来实现乘法运算。 Booth Wallace乘法器的优点在于它可以节省乘法器中的加法器的数量,从而减少了硬件的复杂性和功耗。通过使用补码表示乘数,可以将多个乘法和加法操作合并为一组简单的操作。这种算法在计算机乘法运算中被广泛使用,因为它具有较高的效率和更低的延迟。 Booth Wallace乘法器实现过程相对复杂,需要通过状态机以及各种加法运算器进行计算。然而,由于它的高效性和硬件的可优化性,在计算机中被普遍应用。 总结起来,Booth Wallace乘法器是一种在计算机中进行定点乘法运算的有效算法。它通过将乘法转化为一系列的加法运算,利用了二进制补码的特性,从而减少了硬件的复杂性和功耗。这种乘法器在数字逻辑电路中被广泛使用,为计算机高效的运行做出了重要贡献。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lu-ming.xyz

觉得有用的话点个赞吧 :)

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值