IC基础:解决乘法器

叙述说明:

  1. 默认输入输出均为补码
  2. 只考虑逻辑门的延迟,组合逻辑完成算法,暂不考虑时序,若时序不满足,也可采用流水线分解乘法步骤。
  3. 形如dout = a * b (a为被乘数mutiplicant,b为乘数mutiplier)
  4. a,b位宽均为n

普通乘法器

  • mutiplier最高位的权重为(-)的 (2的n-1)次方,其中n-1次方对应mutiplicant左移n-1次方。
  • -(负号)对应补码取反加一:此处分情况讨论, a为正数,-a为负数,-a的补码等于a的补码符号位取反,其余位取反加一,即a的补码所有位取反加一;a为负数,-a为正数,符号位取反,其余位-1取反(但-1取反相当于取反加一,对应十进制中9-(x-1)=9-x+1),因此还是所有位取反加一。此处较绕,建议先理解原码反码补码的关系以及二进制对应十进制中的操作
  • 对一个数进行先移位后取反加一,和先取反加一后移位,是相同的。(100- 10*x)等于(10 * (10-x)),我是这么理解,移位的过程中模也跟着变了。
  • 这篇知乎写的很好,把二进制的操作对应到十进制中
    其中主要理解:
  1. 十进制中的9-x相当于二进制中的~x(取反相当于111…-x,而111…就是进制中最大的数),负数的补码对其原码符号位不变,其余位取反加一(相当于9-x+1,类比适中的例子,十二进制中,前进3等于后退12-3=8)
  2. 一个数负数的补码等于它本身的补码所有位取反加一
  • 操作顺序:
  1. mutiplicant扩展为2*n位
  2. mutiplier逐位选择,根据权重将选择到的mutipliacant进行左移
  3. 将所有结果相加
module common_muti#(
    parameter WIDTH = 4,
    parameter WIDTH_dout = 8
)
(
    input [WIDTH - 1 : 0] a,
    input [WIDTH - 1 : 0] b,
    output [WIDTH_dout - 1 : 0] dout
);
    //===============defination
    wire [WIDTH_dout - 1 : 0]temp_a;
    
    wire [WIDTH_dout - 1 : 0] temp_0;
    wire [WIDTH_dout - 1 : 0] temp_1;
    wire [WIDTH_dout - 1 : 0] temp_2;
    wire [WIDTH_dout - 1 : 0] temp_3;
    
    wire [WIDTH_dout - 1 : 0] temp_a_inv_1;

    //===============output
    assign temp_a = $signed(a);
    assign temp_a_inv_1 = (~temp_a) + 1'b1;
    
    assign temp_0 = b[0] ? temp_a : 'd0;
    assign temp_1 = b[1] ? (temp_a << 1) : 'd0;
    assign temp_2 = b[2] ? (temp_a << 2) : 'd0;
    assign temp_3 = b[3] ? (temp_a_inv_1 << 3) : 'd0;

    assign dout = temp_0 + temp_1 + temp_2 + temp_3;
    
endmodule

testbench:

module tb_common_muti();
    //===================parameter
    parameter PERIOD = 10 / 2;
    parameter WIDTH = 4;
    parameter WIDTH_dout = 8;

    //===================parameter
    reg clk, rst_n;
    reg signed [WIDTH - 1 : 0] a;
    reg signed [WIDTH - 1 : 0] b;
    reg signed [WIDTH_dout - 1 : 0] dout;

    //===================parameter
    initial begin
        clk = 0;
        rst_n = 0;
        #200 rst_n = 1;
	#500 $finish;
    end
    always #PERIOD clk = ~clk;
    initial begin
        a = 0;
        @(posedge clk) a = -5;
        @(posedge clk) a = 3;
        @(posedge clk) a = 2;
        @(posedge clk) a = -1;
    end
    initial begin
        b = 0;
        @(posedge clk) b = 5;
        @(posedge clk) b = 3;
        @(posedge clk) b = -2;
        @(posedge clk) b = 4'b1111;

    end
    common_muti#(
        .WIDTH (WIDTH),
        .WIDTH_dout (WIDTH_dout)
    )
    u_common_muti
    (
        .a(a),
        .b(b),
        .dout(dout)
    );
`ifdef FSDB
initial begin
	$fsdbDumpfile("tb_common_muti.fsdb");
	$fsdbDumpvars;
    $fsdbDumpMDA();
end
`endif
endmodule 
  • VCS+Verdi 仿真结果:

在这里插入图片描述
schematic:
在这里插入图片描述

  • 说明
  1. 此方法没有对每一个待加的数进行模块复用,用面积换了时间,反之如果需要时间换面积,将temp复用即可。
  2. dout直接写为4个多bit数的加法,大概率综合出以下电路:(偷的)
    在这里插入图片描述
    MU为一个与门加一个全加器,此电路延迟高(最高延迟为一个与门+10个加法器),当mutiplicant和mutiplier的位宽变多的时候,此电路将延迟非常大。
  • 优化延迟
    加法器部分可以采用超前进位加法器Wallace_tree型加法器。但超前进位加法器不能过多bit进入Carry_look_array进行计算,电路扇入扇出过大,可能超出电路带负载能力(扇入扇出的具体原理笔者也没有深究,但对于TTL逻辑门而言,fanout一般为10),不过可以级联起来,比如3个4bit超前进位加法器组成一个16bit加法器。

BOOTH乘法器

  1. 扩展mutiplicant为2n
  2. mutiplier最低位加0,最高位加两位符号位
  3. 每次判断mutiplier[2 : 0],根据编码选择mutiplicant的操作数,然后右移两位,
  4. 扩展后的mutiplicant左移两位,转移到3,用计数器跳出循环到5
  5. 将所有结果相加
module booth_4radix_muti#(
    parameter WIDTH_a = 8,
    parameter WIDTH_b = 8,
    parameter WIDTH_dout = 16
)
(
    input clk,
    input rst_n,
    input [WIDTH_a - 1 : 0] a,
    input [WIDTH_b - 1 : 0] b,
    output [WIDTH_dout - 1 : 0] dout
);
//===============parameter

//===============defination
    wire [WIDTH_dout - 1 : 0] temp_a;
    wire [WIDTH_b + 2 : 0] temp_b;

    reg [WIDTH_dout - 1 : 0] temp0;
    reg [WIDTH_dout - 1 : 0] temp1;
    reg [WIDTH_dout - 1 : 0] temp2;
    reg [WIDTH_dout - 1 : 0] temp3;
    reg [WIDTH_dout - 1 : 0] temp4;


    wire [WIDTH_dout - 1 : 0] temp012_s;
    wire [WIDTH_dout - 1 : 0] temp012_co;

    wire [WIDTH_dout - 1 : 0] temp0123_s;
    wire [WIDTH_dout - 1 : 0] temp0123_co;
    
    wire [WIDTH_dout - 1 : 0] temp01234_s;
    wire [WIDTH_dout - 1 : 0] temp01234_co;
    
    wire [WIDTH_dout - 1 : 0] a_add1;
    wire [WIDTH_dout - 1 : 0] a_add2;

    wire [WIDTH_dout - 1 : 0] a_sub1;
    wire [WIDTH_dout - 1 : 0] a_sub2;
//===============output

    assign a_add1 = temp_a;
    assign a_add2 = temp_a << 1;
    assign a_sub1 = ~temp_a + 1'b1;
    assign a_sub2 = ~(temp_a << 1) + 1'b1;


    assign temp_a = {{(WIDTH_b){a[WIDTH_a - 1]}}, a};
    assign temp_b = {{2{b[WIDTH_b - 1]}}, b, 1'b0};
    
    always@(*) begin 
        case(temp_b[2 : 0])
            3'b000, 3'b111 : temp0 = 'd0;
            3'b010, 3'b001 : temp0 = a_add1;
            3'b011 : temp0 = a_add2;
            3'b101, 3'b110 : temp0 = a_sub1;
            3'b100 : temp0 = a_sub2;
            default : temp0 = 'd0;
        endcase
    end

    always@(*) begin 
        case(temp_b[4 : 2])
            3'b000, 3'b111 : temp1 = 'd0;
            3'b010, 3'b001 : temp1 = a_add1;
            3'b011 : temp1 = a_add2;
            3'b101, 3'b110 : temp1 = a_sub1;
            3'b100 : temp1 = a_sub2;
            default : temp1 = 'd0;
        endcase
    end

    always@(*) begin 
        case(temp_b[6 : 4])
            3'b000, 3'b111 : temp2 = 'd0;
            3'b010, 3'b001 : temp2 = a_add1;
            3'b011 : temp2 = a_add2;
            3'b101, 3'b110 : temp2 = a_sub1;
            3'b100 : temp2 = a_sub2;
            default : temp2 = 'd0;
        endcase
    end

    always@(*) begin 
        case(temp_b[8 : 6])
            3'b000, 3'b111 : temp3 = 'd0;
            3'b010, 3'b001 : temp3 = a_add1;
            3'b011 : temp3 = a_add2;
            3'b101, 3'b110 : temp3 = a_sub1;
            3'b100 : temp3 = a_sub2;
            default : temp3 = 'd0;
        endcase
    end

    always@(*) begin 
        case(temp_b[10 : 8])
            3'b000, 3'b111 : temp4 = 'd0;
            3'b010, 3'b001 : temp4 = a_add1;
            3'b011 : temp4 = a_add2;
            3'b101, 3'b110 : temp4 = a_sub1;
            3'b100 : temp4 = a_sub2;
            default : temp4 = 'd0;
        endcase
    end
    Wallace_tree#(
        .WIDTH(WIDTH_dout)
    )
    u_wallace0
    (
        .a(temp0),
        .b(temp1 << 2),
        .ci(temp2 << 4),
        .s(temp012_s),
        .co(temp012_co)
    );

    Wallace_tree#(
        .WIDTH(WIDTH_dout)
    )
    u_wallace1
    (
        .a(temp3 << 6),
        .b(temp012_s),
        .ci(temp012_co),
        .s(temp0123_s),
        .co(temp0123_co)
    );

    Wallace_tree#(
        .WIDTH(WIDTH_dout)
    )
    u_wallace2
    (
        .a(temp4 << 8),
        .b(temp0123_s),
        .ci(temp0123_co),
        .s(temp01234_s),
        .co(temp01234_co)
    );
    assign dout = temp01234_s + temp01234_co;
    

endmodule

module Carry_saved_adder(
    input a,
    input b,
    input ci,
    output s,
    output co
);
    assign s = a ^ b ^ ci;
    assign co = a & b |(ci & (a | b));
endmodule

module Wallace_tree#(
    parameter WIDTH = 16
)
(
    input [WIDTH - 1 : 0] a,
    input [WIDTH - 1 : 0] b,
    input [WIDTH - 1 : 0] ci,
    output [WIDTH - 1 : 0] s,
    output [WIDTH - 1 : 0] co
);    
    wire [WIDTH - 1 : 0] co_w;
    genvar i;
    generate
    for(i = 0; i < WIDTH; i = i + 1)begin : loop
        Carry_saved_adder u_carry(
            .a(a[i]),
            .b(b[i]),
            .ci(ci[i]),
            .s(s[i]),
            .co(co_w[i])
        );
    end
    endgenerate
    assign co = co_w << 1;
endmodule 

Testbench:

module tb_booth_4radix_muti();
    //===================parameter
    parameter PERIOD = 10 / 2;
    parameter WIDTH_a = 8;
    parameter WIDTH_b = 8;
    parameter WIDTH_dout = 16;

    //===================parameter
    reg clk, rst_n;
    reg signed [WIDTH_a - 1 : 0] a;
    reg signed [WIDTH_b - 1 : 0] b;
    reg signed [WIDTH_dout - 1 : 0] dout;

    //===================parameter
    initial begin
        clk = 0;
        rst_n = 0;
        #200 rst_n = 1;
	#500 $finish;
    end
    always #PERIOD clk = ~clk;
    
    initial begin
        a = 0;
        @(posedge clk) a = -5;
        @(posedge clk) a = 3;
        @(posedge clk) a = 2;
        @(posedge clk) a = -1;
    end
    initial begin
        b = 0;
        @(posedge clk) b = 5;
        @(posedge clk) b = 3;
        @(posedge clk) b = -2;
        @(posedge clk) b = -1;

    end
    booth_4radix_muti#(
        .WIDTH_a (WIDTH_a),
        .WIDTH_b (WIDTH_b),
        .WIDTH_dout (WIDTH_dout)
    )
    u_booth_4radix_muti
    (
        .clk (clk),
        .rst_n(rst_n),
        .a(a),
        .b(b),
        .dout(dout)
    );
`ifdef FSDB
initial begin
	$fsdbDumpfile("tb_booth_4radix_muti.fsdb");
	$fsdbDumpvars;
    $fsdbDumpMDA();
end
`endif

endmodule 
  • VCS+Verdi 仿真结果:
    在这里插入图片描述

  • schematic:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    =================================================== 2022.03.17关于乘法器先告一段落,有机会再优化,写流水线什么的。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值